[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)
|
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(
|
private val CallableReferenceLoweringPhase = makeJsPhase(
|
||||||
{ context, module -> CallableReferenceLowering(context).lower(module) },
|
{ context, module -> CallableReferenceLowering(context).lower(module) },
|
||||||
name = "CallableReferenceLowering",
|
name = "CallableReferenceLowering",
|
||||||
description = "Handle callable references",
|
description = "Handle callable references",
|
||||||
prerequisite = setOf(SuspendFunctionsLoweringPhase, LocalDeclarationsLoweringPhase, LocalDelegatedPropertiesLoweringPhase)
|
prerequisite = setOf(
|
||||||
|
SuspendFunctionsLoweringPhase,
|
||||||
|
LocalDeclarationsLoweringPhase,
|
||||||
|
LocalDelegatedPropertiesLoweringPhase,
|
||||||
|
PrivateMembersLoweringPhase
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val DefaultArgumentStubGeneratorPhase = makeJsPhase(
|
private val DefaultArgumentStubGeneratorPhase = makeJsPhase(
|
||||||
@@ -346,6 +357,7 @@ val jsPhases = listOf(
|
|||||||
InnerClassesLoweringPhase,
|
InnerClassesLoweringPhase,
|
||||||
InnerClassConstructorCallsLoweringPhase,
|
InnerClassConstructorCallsLoweringPhase,
|
||||||
SuspendFunctionsLoweringPhase,
|
SuspendFunctionsLoweringPhase,
|
||||||
|
PrivateMembersLoweringPhase,
|
||||||
CallableReferenceLoweringPhase,
|
CallableReferenceLoweringPhase,
|
||||||
DefaultArgumentStubGeneratorPhase,
|
DefaultArgumentStubGeneratorPhase,
|
||||||
DefaultParameterInjectorPhase,
|
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
|
// IGNORE_BACKEND: JVM_IR
|
||||||
open class B {
|
open class B {
|
||||||
val p = "OK"
|
val p = "OK"
|
||||||
|
|||||||
+5
@@ -23790,6 +23790,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/traits/noPrivateDelegation.kt");
|
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")
|
@TestMetadata("syntheticAccessor.kt")
|
||||||
public void testSyntheticAccessor() throws Exception {
|
public void testSyntheticAccessor() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
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");
|
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")
|
@TestMetadata("syntheticAccessor.kt")
|
||||||
public void testSyntheticAccessor() throws Exception {
|
public void testSyntheticAccessor() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
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");
|
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")
|
@TestMetadata("syntheticAccessor.kt")
|
||||||
public void testSyntheticAccessor() throws Exception {
|
public void testSyntheticAccessor() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
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");
|
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")
|
@TestMetadata("syntheticAccessor.kt")
|
||||||
public void testSyntheticAccessor() throws Exception {
|
public void testSyntheticAccessor() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
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");
|
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")
|
@TestMetadata("syntheticAccessor.kt")
|
||||||
public void testSyntheticAccessor() throws Exception {
|
public void testSyntheticAccessor() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
runTest("compiler/testData/codegen/box/traits/syntheticAccessor.kt");
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1327
|
// EXPECTED_REACHABLE_NODES: 1327
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val b = B()
|
val b = B()
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1235
|
// EXPECTED_REACHABLE_NODES: 1235
|
||||||
class X(private val x: String) {
|
class X(private val x: String) {
|
||||||
operator fun getValue(thisRef: Any?, property: Any): String = x
|
operator fun getValue(thisRef: Any?, property: Any): String = x
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1286
|
// EXPECTED_REACHABLE_NODES: 1286
|
||||||
open class A {
|
open class A {
|
||||||
private val `.` = "A"
|
private val `.` = "A"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1291
|
// EXPECTED_REACHABLE_NODES: 1291
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -240,8 +240,8 @@ public class Long internal constructor(
|
|||||||
public override fun toFloat(): Float = toDouble().toFloat()
|
public override fun toFloat(): Float = toDouble().toFloat()
|
||||||
public override fun toDouble(): Double = toNumber()
|
public override fun toDouble(): Double = toNumber()
|
||||||
|
|
||||||
// TODO: is it still needed?
|
// This method is used by `toString()`
|
||||||
private fun valueOf() = toDouble()
|
internal fun valueOf() = toDouble()
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean = other is Long && equalsLong(other)
|
override fun equals(other: Any?): Boolean = other is Long && equalsLong(other)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user