diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index d2f3fe61753..49852f91ade 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -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, 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 new file mode 100644 index 00000000000..5c89c43a243 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrivateMembersLowering.kt @@ -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() + + 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 + } + +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/traits/privateInterfaceMethod.kt b/compiler/testData/codegen/box/traits/privateInterfaceMethod.kt new file mode 100644 index 00000000000..eb110832043 --- /dev/null +++ b/compiler/testData/codegen/box/traits/privateInterfaceMethod.kt @@ -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() \ No newline at end of file diff --git a/compiler/testData/codegen/box/traits/traitWithPrivateExtension.kt b/compiler/testData/codegen/box/traits/traitWithPrivateExtension.kt index 18d41bca32a..18087b76de0 100644 --- a/compiler/testData/codegen/box/traits/traitWithPrivateExtension.kt +++ b/compiler/testData/codegen/box/traits/traitWithPrivateExtension.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR open class B { val p = "OK" diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 3385ac7044c..bed35e6731d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index cc918fa5cfd..9c8a05161f4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index ebd256e3021..bbdb1e82687 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index 1ebf8229e94..83491a5f951 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -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"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 5feaadb7726..f76cceaafdd 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -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"); diff --git a/js/js.translator/testData/box/delegateProperty/clashingNameInSubclass.kt b/js/js.translator/testData/box/delegateProperty/clashingNameInSubclass.kt index 8a8aaeae150..976c29dce0d 100644 --- a/js/js.translator/testData/box/delegateProperty/clashingNameInSubclass.kt +++ b/js/js.translator/testData/box/delegateProperty/clashingNameInSubclass.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1327 fun box(): String { val b = B() diff --git a/js/js.translator/testData/box/delegateProperty/peculiarName.kt b/js/js.translator/testData/box/delegateProperty/peculiarName.kt index b519fbb998b..a898c039ffe 100644 --- a/js/js.translator/testData/box/delegateProperty/peculiarName.kt +++ b/js/js.translator/testData/box/delegateProperty/peculiarName.kt @@ -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 diff --git a/js/js.translator/testData/box/expression/identifierClash/privateDeclarations.kt b/js/js.translator/testData/box/expression/identifierClash/privateDeclarations.kt index 40e56f3873d..d1653d13cf1 100644 --- a/js/js.translator/testData/box/expression/identifierClash/privateDeclarations.kt +++ b/js/js.translator/testData/box/expression/identifierClash/privateDeclarations.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1286 open class A { private val `.` = "A" diff --git a/js/js.translator/testData/box/propertyOverride/overloadPrivateVal.kt b/js/js.translator/testData/box/propertyOverride/overloadPrivateVal.kt index 9a3f5c9ec48..9ec1924a554 100644 --- a/js/js.translator/testData/box/propertyOverride/overloadPrivateVal.kt +++ b/js/js.translator/testData/box/propertyOverride/overloadPrivateVal.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1291 package foo diff --git a/libraries/stdlib/js/irRuntime/long.kt b/libraries/stdlib/js/irRuntime/long.kt index 5bca4a13ee1..4ea280d3418 100644 --- a/libraries/stdlib/js/irRuntime/long.kt +++ b/libraries/stdlib/js/irRuntime/long.kt @@ -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)