From c5e25a038289e93edbafb58325b7a93cb77441dc Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Wed, 20 Nov 2019 21:34:25 +0300 Subject: [PATCH] [JS IR] Make `PrivateMembersLowering` global to handle inlined private references correctly - Fixes [KT-33334] --- .../ir/backend/js/JsIrBackendContext.kt | 2 + .../js/lower/PrivateMembersLowering.kt | 114 ++++++++++-------- .../ir/semantics/IrBoxJsTestGenerated.java | 5 + .../js/test/semantics/BoxJsTestGenerated.java | 5 + .../inlineMultiFile/privateVarFromInline.kt | 40 ++++++ 5 files changed, 114 insertions(+), 52 deletions(-) create mode 100644 js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index 558cb65ff39..0600f90c1f1 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -51,6 +51,8 @@ class JsIrBackendContext( override val transformedFunction = mutableMapOf() override val lateinitNullableFields = mutableMapOf() + val memberMap = mutableMapOf() + override val builtIns = module.builtIns override var inVerbosePhase: Boolean = false diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrivateMembersLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrivateMembersLowering.kt index 9217c6f6120..36d47c17ad6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrivateMembersLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrivateMembersLowering.kt @@ -21,7 +21,7 @@ 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.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols @@ -33,13 +33,11 @@ import org.jetbrains.kotlin.name.Name private val STATIC_THIS_PARAMETER = object : IrDeclarationOriginImpl("STATIC_THIS_PARAMETER") {} class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass { - private val memberMap = mutableMapOf() + private val memberMap = context.memberMap override fun lower(irFile: IrFile) { transformPrivateDeclarations(irFile) transformPrivateUseSites(irFile) - - memberMap.clear() } private fun transformPrivateDeclarations(irFile: IrFile) { @@ -49,7 +47,6 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass declaration.declarations.transformFlat { when (it) { is IrSimpleFunction -> transformMemberToStaticFunction(it)?.let { staticFunction -> - memberMap[it.symbol] = staticFunction listOf(staticFunction) } is IrProperty -> listOf(it.apply { @@ -69,7 +66,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass override fun visitCall(expression: IrCall): IrExpression { super.visitCall(expression) - return memberMap[expression.symbol]?.let { + return getOrPutStaticFunction(expression.symbol)?.let { transformPrivateToStaticCall(expression, it) } ?: expression } @@ -77,7 +74,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { super.visitFunctionReference(expression) - return memberMap[expression.symbol]?.let { + return getOrPutStaticFunction(expression.symbol)?.let { transformPrivateToStaticReference(expression) { IrFunctionReferenceImpl( expression.startOffset, expression.endOffset, @@ -92,7 +89,10 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass override fun visitPropertyReference(expression: IrPropertyReference): IrExpression { super.visitPropertyReference(expression) - return if (expression.getter in memberMap || expression.setter in memberMap) { + val staticGetter = expression.getter?.let { getOrPutStaticFunction(it) } + val staticSetter = expression.setter?.let { getOrPutStaticFunction(it) } + + return if (staticGetter != null || staticSetter != null) { transformPrivateToStaticReference(expression) { IrPropertyReferenceImpl( expression.startOffset, expression.endOffset, @@ -100,8 +100,8 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass expression.symbol, // TODO remap property symbol based on remapped getter/setter? expression.typeArgumentsCount, expression.field, - memberMap[expression.getter]?.symbol ?: expression.getter, - memberMap[expression.setter]?.symbol ?: expression.setter, + staticGetter?.symbol ?: expression.getter, + staticSetter?.symbol ?: expression.setter, expression.origin ) } @@ -127,6 +127,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass return newExpression } + private fun transformPrivateToStaticReference( expression: IrCallableReference, builder: () -> IrCallableReference @@ -147,28 +148,11 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass }, null) } - private fun transformAccessor(accessor: IrSimpleFunction) = - transformMemberToStaticFunction(accessor)?.also { memberMap[accessor.symbol] = it } ?: accessor + private fun transformAccessor(accessor: IrSimpleFunction) = transformMemberToStaticFunction(accessor) ?: 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 = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect, - isFakeOverride = origin == IrDeclarationOrigin.FAKE_OVERRIDE - ).also { - descriptor.bind(it) - it.parent = parent - it.correspondingPropertySymbol = correspondingPropertySymbol - } - } + val staticFunction = getOrPutStaticFunction(function.symbol) ?: return null // Detach old function from corresponding property val correspondingProperty = function.correspondingPropertySymbol?.owner @@ -179,29 +163,6 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass } } - 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 @@ -220,4 +181,53 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass return staticFunction } + private fun getOrPutStaticFunction(functionSymbol: IrFunctionSymbol): IrSimpleFunction? { + val function = functionSymbol.owner + if (function !is IrSimpleFunction) return null + if (function.visibility != Visibilities.PRIVATE || function.dispatchReceiverParameter == null) return null + + return memberMap.getOrPut(function.symbol) { + val descriptor = WrappedSimpleFunctionDescriptor() + val symbol = IrSimpleFunctionSymbolImpl(descriptor) + val staticFunction = function.run { + IrFunctionImpl( + startOffset, endOffset, origin, + symbol, name, visibility, modality, + returnType, + isInline = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect, + isFakeOverride = origin == IrDeclarationOrigin.FAKE_OVERRIDE + ).also { + descriptor.bind(it) + it.parent = parent + it.correspondingPropertySymbol = correspondingPropertySymbol + } + } + + 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, + isCrossinline = false, + isNoinline = false + ).also { + thisDesc.bind(it) + it.parent = staticFunction + } + + staticFunction.valueParameters += function.valueParameters.map { it.copyTo(staticFunction, index = it.index + 1) } + + staticFunction + } + } + } \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java index 3ad3351e8cc..5e382b6c0ca 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java @@ -4249,6 +4249,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/inlineMultiFile/lambdaInLambdaNoInline.kt"); } + @TestMetadata("privateVarFromInline.kt") + public void testPrivateVarFromInline() throws Exception { + runTest("js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt"); + } + @TestMetadata("regeneratedLambdaName.kt") public void testRegeneratedLambdaName() throws Exception { runTest("js/js.translator/testData/box/inlineMultiFile/regeneratedLambdaName.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 54d333e8e93..179b994721a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -4264,6 +4264,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/inlineMultiFile/lambdaInLambdaNoInline.kt"); } + @TestMetadata("privateVarFromInline.kt") + public void testPrivateVarFromInline() throws Exception { + runTest("js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt"); + } + @TestMetadata("regeneratedLambdaName.kt") public void testRegeneratedLambdaName() throws Exception { runTest("js/js.translator/testData/box/inlineMultiFile/regeneratedLambdaName.kt"); diff --git a/js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt b/js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt new file mode 100644 index 00000000000..d1cda654fbc --- /dev/null +++ b/js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt @@ -0,0 +1,40 @@ +// FILE: f.kt +package foo + +/** + * KT-33334 + */ + +internal fun foo(c: C) = c.getter() + +internal fun bar(c: C) { c.setter("OK") } + +// FILE: c.kt +package foo + +var log = "" + +internal class C { + private var tokenPosition: String = "FAIL" + get() { logg("G"); return field } + set(value) { logg("S"); field = value } + + private fun logg(s: String) { log += s } + + internal inline fun setter(s: String) { tokenPosition = s } + internal inline fun getter(): String = tokenPosition +} + +// FILE: m.kt +package foo + +fun box(): String { + + val c = C() + + bar(c) + val r = foo(c) + if (log != "SG") return "FAIL1: $log" + + return r +} \ No newline at end of file