diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineClassDeclarationLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineClassDeclarationLowering.kt index 5c8c782a254..c6781ef62db 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineClassDeclarationLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineClassDeclarationLowering.kt @@ -39,8 +39,9 @@ class InlineClassLowering(val context: CommonBackendContext) { } } - private fun transformConstructor(irConstructor: IrConstructor): List? { - if (irConstructor.isPrimary) return null + private fun transformConstructor(irConstructor: IrConstructor): List { + if (irConstructor.isPrimary) + return transformPrimaryConstructor(irConstructor) // Secondary constructors are lowered into static function val result = getOrCreateStaticMethod(irConstructor) @@ -67,6 +68,72 @@ class InlineClassLowering(val context: CommonBackendContext) { return listOf(function, staticMethod) } + private fun transformPrimaryConstructor(irConstructor: IrConstructor): List { + val klass = irConstructor.parentAsClass + val inlineClassType = klass.defaultType + val initFunction = getOrCreateStaticMethod(irConstructor).also { + it.returnType = inlineClassType + } + var delegatingCtorCall: IrDelegatingConstructorCall? = null + var setMemberField: IrSetField? = null + + initFunction.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) { + val origParameterSymbol = irConstructor.valueParameters.single().symbol + statements += context.createIrBuilder(initFunction.symbol).irBlockBody(initFunction) { + val builder = this + fun unboxedInlineClassValue() = builder.irReinterpretCast( + builder.irGet(initFunction.valueParameters.single()), + type = klass.defaultType, + ) + + (irConstructor.body as IrBlockBody).deepCopyWithSymbols(initFunction).statements.forEach { statement -> + +statement.transformStatement(object : IrElementTransformerVoid() { + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { + delegatingCtorCall = expression.deepCopyWithSymbols(irConstructor) + return builder.irBlock {} // Removing delegating constructor call + } + + override fun visitSetField(expression: IrSetField): IrExpression { + val isMemberFieldSet = expression.symbol.owner.parent == klass + if (isMemberFieldSet) { + setMemberField = expression.deepCopyWithSymbols(irConstructor) + } + expression.transformChildrenVoid() + if (isMemberFieldSet) { + return expression.value + } + return expression + } + + override fun visitGetField(expression: IrGetField): IrExpression { + expression.transformChildrenVoid() + if (expression.symbol.owner.parent == klass) + return builder.irGet(initFunction.valueParameters.single()) + return expression + } + + override fun visitGetValue(expression: IrGetValue): IrExpression { + expression.transformChildrenVoid() + if (expression.symbol.owner.parent == klass) + return unboxedInlineClassValue() + if (expression.symbol == origParameterSymbol) + return builder.irGet(initFunction.valueParameters.single()) + return expression + } + }) + } + +irReturn(unboxedInlineClassValue()) + }.statements + } + + irConstructor.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) { + statements += delegatingCtorCall!! + statements += setMemberField!! + } + + return listOf(irConstructor, initFunction) + } + private fun transformConstructorBody(irConstructor: IrConstructor, staticMethod: IrSimpleFunction) { if (irConstructor.isPrimary) return // TODO error() maybe? @@ -232,7 +299,7 @@ class InlineClassLowering(val context: CommonBackendContext) { override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { expression.transformChildrenVoid(this) val function = expression.symbol.owner - if (!function.parentAsClass.isInline || function.isPrimary) { + if (!function.parentAsClass.isInline) { return expression } @@ -263,7 +330,6 @@ class InlineClassLowering(val context: CommonBackendContext) { val klass = function.parentAsClass return when { !klass.isInline -> expression - function.isPrimary -> irConstructorCall(expression, function.symbol) else -> irCall(expression, getOrCreateStaticMethod(function)) } } 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 30d87f6ff31..871a74b222f 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 @@ -370,6 +370,13 @@ private val propertyAccessorInlinerLoweringPhase = makeBodyLoweringPhase( description = "[Optimization] Inline property accessors" ) +private val copyPropertyAccessorBodiesLoweringPass = makeDeclarationTransformerPhase( + ::CopyAccessorBodyLowerings, + name = "CopyAccessorBodyLowering", + description = "Copy accessor bodies so that ist can be safely read in PropertyAccessorInlineLowering", + prerequisite = setOf(propertyAccessorInlinerLoweringPhase) +) + private val foldConstantLoweringPhase = makeBodyLoweringPhase( { FoldConstantLowering(it, true) }, name = "FoldConstantLowering", @@ -603,6 +610,12 @@ private val secondaryFactoryInjectorLoweringPhase = makeBodyLoweringPhase( prerequisite = setOf(innerClassesLoweringPhase) ) +private val constLoweringPhase = makeBodyLoweringPhase( + ::ConstLowering, + name = "ConstLowering", + description = "Wrap Long and Char constants into constructor invocation" +) + private val inlineClassDeclarationLoweringPhase = makeDeclarationTransformerPhase( { InlineClassLowering(it).inlineClassDeclarationLowering }, name = "InlineClassDeclarationLowering", @@ -612,7 +625,12 @@ private val inlineClassDeclarationLoweringPhase = makeDeclarationTransformerPhas private val inlineClassUsageLoweringPhase = makeBodyLoweringPhase( { InlineClassLowering(it).inlineClassUsageLowering }, name = "InlineClassUsageLowering", - description = "Handle inline class usages" + description = "Handle inline class usages", + prerequisite = setOf( + // Const lowering generates inline class constructors for unsigned integers + // which should be lowered by this lowering + constLoweringPhase + ) ) private val autoboxingTransformerPhase = makeBodyLoweringPhase( @@ -640,12 +658,6 @@ private val primitiveCompanionLoweringPhase = makeBodyLoweringPhase( description = "Replace common companion object access with platform one" ) -private val constLoweringPhase = makeBodyLoweringPhase( - ::ConstLowering, - name = "ConstLowering", - description = "Wrap Long and Char constants into constructor invocation" -) - private val callsLoweringPhase = makeBodyLoweringPhase( ::CallsLowering, name = "CallsLowering", @@ -743,6 +755,7 @@ val loweringList = listOf( propertyLazyInitLoweringPhase, removeInitializersForLazyProperties, propertyAccessorInlinerLoweringPhase, + copyPropertyAccessorBodiesLoweringPass, foldConstantLoweringPhase, privateMembersLoweringPhase, privateMemberUsagesLoweringPhase, @@ -763,11 +776,11 @@ val loweringList = listOf( secondaryConstructorLoweringPhase, secondaryFactoryInjectorLoweringPhase, classReferenceLoweringPhase, + constLoweringPhase, inlineClassDeclarationLoweringPhase, inlineClassUsageLoweringPhase, autoboxingTransformerPhase, blockDecomposerLoweringPhase, - constLoweringPhase, objectDeclarationLoweringPhase, invokeStaticInitializersPhase, objectUsageLoweringPhase, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CopyAccessorBodyLowerings.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CopyAccessorBodyLowerings.kt new file mode 100644 index 00000000000..b96406de4ae --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CopyAccessorBodyLowerings.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.CommonBackendContext +import org.jetbrains.kotlin.backend.common.DeclarationTransformer +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.IrBlockBody +import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols + +// Copies property accessors and initializers so that the PropertyAccessorInilineLowering may access them safely. +class CopyAccessorBodyLowerings(private val context: CommonBackendContext) : DeclarationTransformer { + override fun transformFlat(declaration: IrDeclaration): List? { + if (declaration is IrSimpleFunction && declaration.correspondingPropertySymbol != null) { + declaration.body?.let { originalBody -> + declaration.body = context.irFactory.createBlockBody(originalBody.startOffset, originalBody.endOffset) { + statements += (originalBody.deepCopyWithSymbols(declaration) as IrBlockBody).statements + } + } + } + + if (declaration is IrField) { + declaration.initializer?.let { originalBody -> + declaration.initializer = context.irFactory.createExpressionBody(originalBody.startOffset, originalBody.endOffset) { + this.expression = originalBody.expression.deepCopyWithSymbols(declaration) + } + } + } + + return null + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index 3cda932ac69..2889b4cc274 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -161,39 +161,36 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer { + val refForExternalClass = context.getRefForExternalClass(klass) + val varargParameterIndex = expression.symbol.owner.varargParameterIndex() + if (varargParameterIndex == -1) { + JsNew(refForExternalClass, arguments) + } else { + val argumentsAsSingleArray = argumentsWithVarargAsSingleArray( + JsNullLiteral(), + arguments, + varargParameterIndex + ) + JsNew( + JsInvocation( + JsNameRef("apply", JsNameRef("bind", JsNameRef("Function"))), + refForExternalClass, + argumentsAsSingleArray + ), + emptyList() + ) + } } - // Argument value constructs unboxed inline class instance - arguments.single() - } else { - when { - klass.isEffectivelyExternal() -> { - val refForExternalClass = context.getRefForExternalClass(klass) - val varargParameterIndex = expression.symbol.owner.varargParameterIndex() - if (varargParameterIndex == -1) { - JsNew(refForExternalClass, arguments) - } else { - val argumentsAsSingleArray = argumentsWithVarargAsSingleArray( - JsNullLiteral(), - arguments, - varargParameterIndex - ) - JsNew( - JsInvocation( - JsNameRef("apply", JsNameRef("bind", JsNameRef("Function"))), - refForExternalClass, - argumentsAsSingleArray - ), - emptyList() - ) - } - } - else -> { - val ref = context.getNameForClass(klass).makeRef() - JsNew(ref, arguments) - } + else -> { + val ref = context.getNameForClass(klass).makeRef() + JsNew(ref, arguments) } } } diff --git a/compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt b/compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt index 2889cdc5a44..f3377beced7 100644 --- a/compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt +++ b/compiler/testData/codegen/box/inlineClasses/boxImplDoesNotExecuteInitBlock.kt @@ -1,6 +1,5 @@ // DONT_TARGET_EXACT_BACKEND: WASM // WASM_MUTE_REASON: IGNORED_IN_JS -// IGNORE_BACKEND: JS_IR inline class IC(val i: Int) { init { diff --git a/compiler/testData/codegen/box/inlineClasses/initBlock.kt b/compiler/testData/codegen/box/inlineClasses/initBlock.kt index cda7406d835..8980178f1c6 100644 --- a/compiler/testData/codegen/box/inlineClasses/initBlock.kt +++ b/compiler/testData/codegen/box/inlineClasses/initBlock.kt @@ -1,6 +1,5 @@ // DONT_TARGET_EXACT_BACKEND: WASM // WASM_MUTE_REASON: IGNORED_IN_JS -// IGNORE_BACKEND: JS_IR inline class SingleInitBlock(val s: String) { init {