From 3165556c6b3841d9858571278a790c5156b2461b Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Wed, 25 Dec 2019 14:02:02 +0300 Subject: [PATCH] PrivateMembersLowering --- .../ir/backend/js/JsIrBackendContext.kt | 2 - .../kotlin/ir/backend/js/JsLoweringPhases.kt | 7 + .../kotlin/ir/backend/js/JsMapping.kt | 1 + .../js/lower/PrivateMembersLowering.kt | 245 +++++++++--------- 4 files changed, 132 insertions(+), 123 deletions(-) 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 c401c90df1b..bef49a04d39 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 @@ -50,8 +50,6 @@ class JsIrBackendContext( override val lateinitNullableFields get() = error("Use Mapping.lateInitFieldToNullableField instead") - 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/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index ed0cbf9e693..fe2e08281c6 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 @@ -247,6 +247,12 @@ private val privateMembersLoweringPhase = makeJsModulePhase( description = "Extract private members from classes" ) +private val privateMemberUsagesLoweringPhase = makeJsModulePhase( + ::PrivateMemberBodiesLowering, + name = "PrivateMemberUsagesLowering", + description = "Rewrite the private member usages" +) + private val callableReferenceLoweringPhase = makeJsModulePhase( ::CallableReferenceLowering, name = "CallableReferenceLowering", @@ -486,6 +492,7 @@ val jsPhases = namedIrModulePhase( propertyAccessorInlinerLoweringPhase then foldConstantLoweringPhase then privateMembersLoweringPhase then + privateMemberUsagesLoweringPhase then callableReferenceLoweringPhase then defaultArgumentStubGeneratorPhase then defaultArgumentPatchOverridesPhase then diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt index 55c80833b9f..6da759d0c39 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt @@ -18,4 +18,5 @@ class JsMapping : DefaultMapping() { val objectToGetInstanceFunction = newMapping() val objectToInstanceField = newMapping() val classToSyntheticPrimaryConstructor = newMapping() + val privateMemberToCorrespondingStatic = newMapping() } \ No newline at end of file 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 d5a0d6ee86c..f1f0fa77735 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 @@ -5,10 +5,10 @@ package org.jetbrains.kotlin.ir.backend.js.lower -import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.BodyLoweringPass +import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.declarations.* @@ -17,56 +17,143 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor 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.IrFunctionSymbol +import org.jetbrains.kotlin.ir.expressions.impl.* 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.ir.visitors.transformChildrenVoid 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 = context.memberMap +class PrivateMembersLowering(val context: JsIrBackendContext) : DeclarationTransformer { - override fun lower(irFile: IrFile) { - transformPrivateDeclarations(irFile) - transformPrivateUseSites(irFile) + private var IrFunction.correspondingStatic by context.mapping.privateMemberToCorrespondingStatic + + override fun transformFlat(declaration: IrDeclaration): List? { + return when (declaration) { + is IrSimpleFunction -> transformMemberToStaticFunction(declaration)?.let { staticFunction -> + declaration.correspondingStatic = staticFunction + listOf(staticFunction) + } + is IrProperty -> listOf(declaration.apply { + // Detach old function from corresponding property + this.getter = this.getter?.let { g -> transformAccessor(g) } + this.setter = this.setter?.let { s -> transformAccessor(s) } + }) + else -> null + } } - private fun transformPrivateDeclarations(irFile: IrFile) { - irFile.transformChildrenVoid(object : IrElementTransformerVoid() { - override fun visitClass(declaration: IrClass): IrStatement { - declaration.transformChildrenVoid(this) - declaration.declarations.transformFlat { - when (it) { - is IrSimpleFunction -> transformMemberToStaticFunction(it)?.let { staticFunction -> - listOf(staticFunction) - } - is IrProperty -> listOf(it.apply { - this.getter = this.getter?.let { g -> transformAccessor(g) } - this.setter = this.setter?.let { s -> transformAccessor(s) } - }) - else -> null + 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, + isOperator = isOperator + ).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 + } + + function.correspondingStatic = staticFunction + + staticFunction.valueParameters += function.valueParameters.map { + // TODO better way to avoid copying default value + it.copyTo(staticFunction, index = it.index + 1, defaultValue = null) + } + + 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() + + val parameterTransformer = object : IrElementTransformerVoid() { + override fun visitGetValue(expression: IrGetValue) = parameterMapping[expression.symbol.owner]?.let { + expression.run { IrGetValueImpl(startOffset, endOffset, type, it.symbol, origin) } + } ?: expression + } + + fun IrBody.copyWithParameters(): IrBody { + return deepCopyWithSymbols(staticFunction).also { + it.transform(parameterTransformer, null) + } + } + + function.valueParameters.forEach { + // TODO better way to avoid copying default value + + parameterMapping[it]?.apply { + it.defaultValue?.let { originalDefault -> + defaultValue = IrExpressionBodyImpl(it.startOffset, it.endOffset) { + expression = (originalDefault.copyWithParameters() as IrExpressionBody).expression } } - return declaration } - }) - } + } - private fun transformPrivateUseSites(irFile: IrFile) { - irFile.transform(object : IrElementTransformerVoid() { + function.body?.let { + staticFunction.body = when (it) { + is IrBlockBody -> IrBlockBodyImpl(it.startOffset, it.endOffset) { + statements += (it.copyWithParameters() as IrBlockBody).statements + } + is IrExpressionBody -> IrExpressionBodyImpl(it.startOffset, it.endOffset) { + expression = (it.copyWithParameters() as IrExpressionBody).expression + } + is IrSyntheticBody -> it + else -> error("Unexpected body kind: ${it.javaClass}") + } + } + + return staticFunction + } +} + +class PrivateMemberBodiesLowering(val context: JsIrBackendContext) : BodyLoweringPass { + + private var IrFunction.correspondingStatic by context.mapping.privateMemberToCorrespondingStatic + + override fun lower(irBody: IrBody, container: IrDeclaration) { + irBody.transform(object : IrElementTransformerVoid() { override fun visitCall(expression: IrCall): IrExpression { super.visitCall(expression) - return getOrPutStaticFunction(expression.symbol)?.let { + return expression.symbol.owner.correspondingStatic?.let { transformPrivateToStaticCall(expression, it) } ?: expression } @@ -74,7 +161,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { super.visitFunctionReference(expression) - return getOrPutStaticFunction(expression.symbol)?.let { + return expression.symbol.owner.correspondingStatic?.let { transformPrivateToStaticReference(expression) { IrFunctionReferenceImpl( expression.startOffset, expression.endOffset, @@ -89,8 +176,8 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass override fun visitPropertyReference(expression: IrPropertyReference): IrExpression { super.visitPropertyReference(expression) - val staticGetter = expression.getter?.let { getOrPutStaticFunction(it) } - val staticSetter = expression.setter?.let { getOrPutStaticFunction(it) } + val staticGetter = expression.getter?.owner?.correspondingStatic + val staticSetter = expression.setter?.owner?.correspondingStatic return if (staticGetter != null || staticSetter != null) { transformPrivateToStaticReference(expression) { @@ -147,88 +234,4 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass } }, null) } - - private fun transformAccessor(accessor: IrSimpleFunction) = transformMemberToStaticFunction(accessor) ?: accessor - - private fun transformMemberToStaticFunction(function: IrSimpleFunction): IrSimpleFunction? { - - val staticFunction = getOrPutStaticFunction(function.symbol) ?: return null - - // Detach old function from corresponding property - val correspondingProperty = function.correspondingPropertySymbol?.owner - if (correspondingProperty != null) { - when (function) { - correspondingProperty.getter -> correspondingProperty.getter = staticFunction - correspondingProperty.setter -> correspondingProperty.setter = staticFunction - } - } - - 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 - } - - 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, - isOperator = isOperator - ).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