[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:
Roman Artemev
2018-12-22 13:21:49 +03:00
committed by romanart
parent 75996b1c8e
commit d35b20f764
14 changed files with 252 additions and 8 deletions
@@ -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,
@@ -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"
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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,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
+2 -2
View File
@@ -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)