[JS IR BE] Implement private members lowering to extract private methods from class and transform them into static function
* fix kotlin.Long * update tests
This commit is contained in:
@@ -174,11 +174,22 @@ private val SuspendFunctionsLoweringPhase = makeJsPhase(
|
||||
prerequisite = setOf(UnitMaterializationLoweringPhase, CoroutineIntrinsicLoweringPhase)
|
||||
)
|
||||
|
||||
private val PrivateMembersLoweringPhase = makeJsPhase(
|
||||
{ context, module -> PrivateMembersLowering(context).lower(module) },
|
||||
name = "PrivateMembersLowering",
|
||||
description = "Extract private members from classes"
|
||||
)
|
||||
|
||||
private val CallableReferenceLoweringPhase = makeJsPhase(
|
||||
{ context, module -> CallableReferenceLowering(context).lower(module) },
|
||||
name = "CallableReferenceLowering",
|
||||
description = "Handle callable references",
|
||||
prerequisite = setOf(SuspendFunctionsLoweringPhase, LocalDeclarationsLoweringPhase, LocalDelegatedPropertiesLoweringPhase)
|
||||
prerequisite = setOf(
|
||||
SuspendFunctionsLoweringPhase,
|
||||
LocalDeclarationsLoweringPhase,
|
||||
LocalDelegatedPropertiesLoweringPhase,
|
||||
PrivateMembersLoweringPhase
|
||||
)
|
||||
)
|
||||
|
||||
private val DefaultArgumentStubGeneratorPhase = makeJsPhase(
|
||||
@@ -346,6 +357,7 @@ val jsPhases = listOf(
|
||||
InnerClassesLoweringPhase,
|
||||
InnerClassConstructorCallsLoweringPhase,
|
||||
SuspendFunctionsLoweringPhase,
|
||||
PrivateMembersLoweringPhase,
|
||||
CallableReferenceLoweringPhase,
|
||||
DefaultArgumentStubGeneratorPhase,
|
||||
DefaultParameterInjectorPhase,
|
||||
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrPropertyReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
val STATIC_THIS_PARAMETER = object : IrDeclarationOriginImpl("STATIC_THIS_PARAMETER") {}
|
||||
|
||||
class PrivateMembersLowering(val context: JsIrBackendContext) : ClassLoweringPass {
|
||||
private val memberMap = mutableMapOf<IrSimpleFunction, IrSimpleFunction>()
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
|
||||
|
||||
irClass.declarations.transformFlat {
|
||||
when (it) {
|
||||
is IrSimpleFunction -> transformMemberToStaticFunction(it)?.let { staticFunction ->
|
||||
memberMap[it] = staticFunction
|
||||
listOf(staticFunction)
|
||||
}
|
||||
is IrProperty -> listOf(it.apply {
|
||||
getter = getter?.let { g -> transformAccessor(g) }
|
||||
setter = setter?.let { s -> transformAccessor(s) }
|
||||
})
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
irClass.transform(object : IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
super.visitCall(expression)
|
||||
|
||||
return memberMap[expression.symbol.owner]?.let {
|
||||
transformPrivateToStaticCall(expression, it)
|
||||
} ?: expression
|
||||
}
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference) = memberMap[expression.symbol.owner]?.let {
|
||||
transformPrivateToStaticReference(expression) {
|
||||
IrFunctionReferenceImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
expression.type,
|
||||
it.symbol, it.descriptor,
|
||||
expression.typeArgumentsCount, expression.valueArgumentsCount,
|
||||
expression.origin
|
||||
)
|
||||
}
|
||||
} ?: expression
|
||||
|
||||
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
||||
return if (expression.getter?.owner in memberMap || expression.setter?.owner in memberMap) {
|
||||
transformPrivateToStaticReference(expression) {
|
||||
IrPropertyReferenceImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
expression.type,
|
||||
expression.descriptor,
|
||||
expression.typeArgumentsCount,
|
||||
expression.field,
|
||||
memberMap[expression.getter?.owner]?.symbol ?: expression.getter,
|
||||
memberMap[expression.setter?.owner]?.symbol ?: expression.setter,
|
||||
expression.origin
|
||||
)
|
||||
}
|
||||
} else expression
|
||||
}
|
||||
|
||||
|
||||
private fun transformPrivateToStaticCall(expression: IrCall, staticTarget: IrSimpleFunction): IrCall {
|
||||
val newExpression = IrCallImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
expression.type,
|
||||
staticTarget.symbol, staticTarget.descriptor,
|
||||
expression.typeArgumentsCount,
|
||||
expression.origin,
|
||||
expression.superQualifierSymbol
|
||||
)
|
||||
|
||||
newExpression.extensionReceiver = expression.extensionReceiver
|
||||
expression.dispatchReceiver?.let { newExpression.putValueArgument(0, it) }
|
||||
|
||||
for (i in 0 until expression.valueArgumentsCount) {
|
||||
newExpression.putValueArgument(i + 1, expression.getValueArgument(i))
|
||||
}
|
||||
newExpression.copyTypeArgumentsFrom(expression)
|
||||
|
||||
return newExpression
|
||||
}
|
||||
private fun transformPrivateToStaticReference(
|
||||
expression: IrCallableReference,
|
||||
builder: () -> IrCallableReference
|
||||
): IrCallableReference {
|
||||
|
||||
val newExpression = builder()
|
||||
|
||||
newExpression.extensionReceiver = expression.extensionReceiver
|
||||
|
||||
newExpression.dispatchReceiver = expression.dispatchReceiver
|
||||
for (i in 0 until expression.valueArgumentsCount) {
|
||||
newExpression.putValueArgument(i, expression.getValueArgument(i))
|
||||
}
|
||||
newExpression.copyTypeArgumentsFrom(expression)
|
||||
|
||||
return newExpression
|
||||
}
|
||||
}, null)
|
||||
}
|
||||
|
||||
private fun transformAccessor(accessor: IrSimpleFunction) =
|
||||
transformMemberToStaticFunction(accessor)?.also { memberMap[accessor] = it } ?: accessor
|
||||
|
||||
private fun transformMemberToStaticFunction(function: IrSimpleFunction): IrSimpleFunction? {
|
||||
|
||||
if (function.visibility != Visibilities.PRIVATE || function.dispatchReceiverParameter == null) return null
|
||||
|
||||
val descriptor = WrappedSimpleFunctionDescriptor()
|
||||
val symbol = IrSimpleFunctionSymbolImpl(descriptor)
|
||||
val staticFunction = function.run {
|
||||
IrFunctionImpl(
|
||||
startOffset, endOffset, origin,
|
||||
symbol, name, visibility, modality,
|
||||
returnType,
|
||||
isInline, isExternal, isTailrec, isSuspend
|
||||
).also {
|
||||
descriptor.bind(it)
|
||||
it.parent = parent
|
||||
it.correspondingProperty = correspondingProperty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
staticFunction.typeParameters += function.typeParameters.map { it.deepCopyWithSymbols(staticFunction) }
|
||||
|
||||
staticFunction.extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(staticFunction)
|
||||
val thisDesc = WrappedValueParameterDescriptor()
|
||||
val thisSymbol = IrValueParameterSymbolImpl(thisDesc)
|
||||
staticFunction.valueParameters += IrValueParameterImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
STATIC_THIS_PARAMETER,
|
||||
thisSymbol,
|
||||
Name.identifier("\$this"),
|
||||
0,
|
||||
function.dispatchReceiverParameter!!.type,
|
||||
null,
|
||||
false,
|
||||
false
|
||||
).also {
|
||||
thisDesc.bind(it)
|
||||
it.parent = staticFunction
|
||||
}
|
||||
|
||||
staticFunction.valueParameters += function.valueParameters.map { it.copyTo(staticFunction, index = it.index + 1) }
|
||||
|
||||
val oldParameters =
|
||||
listOfNotNull(function.extensionReceiverParameter, function.dispatchReceiverParameter) + function.valueParameters
|
||||
val newParameters = listOfNotNull(staticFunction.extensionReceiverParameter) + staticFunction.valueParameters
|
||||
assert(oldParameters.size == newParameters.size)
|
||||
|
||||
val parameterMapping = oldParameters.zip(newParameters).toMap()
|
||||
|
||||
staticFunction.body = function.body?.deepCopyWithSymbols(staticFunction)
|
||||
|
||||
staticFunction.transform(object : IrElementTransformerVoid() {
|
||||
override fun visitGetValue(expression: IrGetValue) = parameterMapping[expression.symbol.owner]?.let {
|
||||
expression.run { IrGetValueImpl(startOffset, endOffset, type, it.symbol, origin) }
|
||||
} ?: expression
|
||||
}, null)
|
||||
|
||||
return staticFunction
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
interface A {
|
||||
private fun foo() = "OK"
|
||||
|
||||
public fun bar() = foo()
|
||||
}
|
||||
|
||||
class B : A {
|
||||
private fun foo() = "fail"
|
||||
}
|
||||
|
||||
fun box() = B().bar()
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
open class B {
|
||||
val p = "OK"
|
||||
|
||||
+5
@@ -23790,6 +23790,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/traits/noPrivateDelegation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("privateInterfaceMethod.kt")
|
||||
public void testPrivateInterfaceMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/privateInterfaceMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("syntheticAccessor.kt")
|
||||
public void testSyntheticAccessor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
||||
|
||||
+5
@@ -23790,6 +23790,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/traits/noPrivateDelegation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("privateInterfaceMethod.kt")
|
||||
public void testPrivateInterfaceMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/privateInterfaceMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("syntheticAccessor.kt")
|
||||
public void testSyntheticAccessor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
||||
|
||||
+5
@@ -23795,6 +23795,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/traits/noPrivateDelegation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("privateInterfaceMethod.kt")
|
||||
public void testPrivateInterfaceMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/privateInterfaceMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("syntheticAccessor.kt")
|
||||
public void testSyntheticAccessor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
||||
|
||||
+5
@@ -18270,6 +18270,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/traits/noPrivateDelegation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("privateInterfaceMethod.kt")
|
||||
public void testPrivateInterfaceMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/privateInterfaceMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("syntheticAccessor.kt")
|
||||
public void testSyntheticAccessor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
||||
|
||||
+5
@@ -19320,6 +19320,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/traits/noPrivateDelegation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("privateInterfaceMethod.kt")
|
||||
public void testPrivateInterfaceMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/privateInterfaceMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("syntheticAccessor.kt")
|
||||
public void testSyntheticAccessor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1327
|
||||
fun box(): String {
|
||||
val b = B()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1235
|
||||
class X(private val x: String) {
|
||||
operator fun getValue(thisRef: Any?, property: Any): String = x
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1286
|
||||
open class A {
|
||||
private val `.` = "A"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1291
|
||||
package foo
|
||||
|
||||
|
||||
@@ -240,8 +240,8 @@ public class Long internal constructor(
|
||||
public override fun toFloat(): Float = toDouble().toFloat()
|
||||
public override fun toDouble(): Double = toNumber()
|
||||
|
||||
// TODO: is it still needed?
|
||||
private fun valueOf() = toDouble()
|
||||
// This method is used by `toString()`
|
||||
internal fun valueOf() = toDouble()
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is Long && equalsLong(other)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user