[JS IR] Support init blocks in inline classes
This commit is contained in:
+70
-4
@@ -39,8 +39,9 @@ class InlineClassLowering(val context: CommonBackendContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transformConstructor(irConstructor: IrConstructor): List<IrDeclaration>? {
|
private fun transformConstructor(irConstructor: IrConstructor): List<IrDeclaration> {
|
||||||
if (irConstructor.isPrimary) return null
|
if (irConstructor.isPrimary)
|
||||||
|
return transformPrimaryConstructor(irConstructor)
|
||||||
|
|
||||||
// Secondary constructors are lowered into static function
|
// Secondary constructors are lowered into static function
|
||||||
val result = getOrCreateStaticMethod(irConstructor)
|
val result = getOrCreateStaticMethod(irConstructor)
|
||||||
@@ -67,6 +68,72 @@ class InlineClassLowering(val context: CommonBackendContext) {
|
|||||||
return listOf(function, staticMethod)
|
return listOf(function, staticMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun transformPrimaryConstructor(irConstructor: IrConstructor): List<IrDeclaration> {
|
||||||
|
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) {
|
private fun transformConstructorBody(irConstructor: IrConstructor, staticMethod: IrSimpleFunction) {
|
||||||
if (irConstructor.isPrimary) return // TODO error() maybe?
|
if (irConstructor.isPrimary) return // TODO error() maybe?
|
||||||
|
|
||||||
@@ -232,7 +299,7 @@ class InlineClassLowering(val context: CommonBackendContext) {
|
|||||||
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
val function = expression.symbol.owner
|
val function = expression.symbol.owner
|
||||||
if (!function.parentAsClass.isInline || function.isPrimary) {
|
if (!function.parentAsClass.isInline) {
|
||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +330,6 @@ class InlineClassLowering(val context: CommonBackendContext) {
|
|||||||
val klass = function.parentAsClass
|
val klass = function.parentAsClass
|
||||||
return when {
|
return when {
|
||||||
!klass.isInline -> expression
|
!klass.isInline -> expression
|
||||||
function.isPrimary -> irConstructorCall(expression, function.symbol)
|
|
||||||
else -> irCall(expression, getOrCreateStaticMethod(function))
|
else -> irCall(expression, getOrCreateStaticMethod(function))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -370,6 +370,13 @@ private val propertyAccessorInlinerLoweringPhase = makeBodyLoweringPhase(
|
|||||||
description = "[Optimization] Inline property accessors"
|
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(
|
private val foldConstantLoweringPhase = makeBodyLoweringPhase(
|
||||||
{ FoldConstantLowering(it, true) },
|
{ FoldConstantLowering(it, true) },
|
||||||
name = "FoldConstantLowering",
|
name = "FoldConstantLowering",
|
||||||
@@ -603,6 +610,12 @@ private val secondaryFactoryInjectorLoweringPhase = makeBodyLoweringPhase(
|
|||||||
prerequisite = setOf(innerClassesLoweringPhase)
|
prerequisite = setOf(innerClassesLoweringPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val constLoweringPhase = makeBodyLoweringPhase(
|
||||||
|
::ConstLowering,
|
||||||
|
name = "ConstLowering",
|
||||||
|
description = "Wrap Long and Char constants into constructor invocation"
|
||||||
|
)
|
||||||
|
|
||||||
private val inlineClassDeclarationLoweringPhase = makeDeclarationTransformerPhase(
|
private val inlineClassDeclarationLoweringPhase = makeDeclarationTransformerPhase(
|
||||||
{ InlineClassLowering(it).inlineClassDeclarationLowering },
|
{ InlineClassLowering(it).inlineClassDeclarationLowering },
|
||||||
name = "InlineClassDeclarationLowering",
|
name = "InlineClassDeclarationLowering",
|
||||||
@@ -612,7 +625,12 @@ private val inlineClassDeclarationLoweringPhase = makeDeclarationTransformerPhas
|
|||||||
private val inlineClassUsageLoweringPhase = makeBodyLoweringPhase(
|
private val inlineClassUsageLoweringPhase = makeBodyLoweringPhase(
|
||||||
{ InlineClassLowering(it).inlineClassUsageLowering },
|
{ InlineClassLowering(it).inlineClassUsageLowering },
|
||||||
name = "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(
|
private val autoboxingTransformerPhase = makeBodyLoweringPhase(
|
||||||
@@ -640,12 +658,6 @@ private val primitiveCompanionLoweringPhase = makeBodyLoweringPhase(
|
|||||||
description = "Replace common companion object access with platform one"
|
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(
|
private val callsLoweringPhase = makeBodyLoweringPhase(
|
||||||
::CallsLowering,
|
::CallsLowering,
|
||||||
name = "CallsLowering",
|
name = "CallsLowering",
|
||||||
@@ -743,6 +755,7 @@ val loweringList = listOf<Lowering>(
|
|||||||
propertyLazyInitLoweringPhase,
|
propertyLazyInitLoweringPhase,
|
||||||
removeInitializersForLazyProperties,
|
removeInitializersForLazyProperties,
|
||||||
propertyAccessorInlinerLoweringPhase,
|
propertyAccessorInlinerLoweringPhase,
|
||||||
|
copyPropertyAccessorBodiesLoweringPass,
|
||||||
foldConstantLoweringPhase,
|
foldConstantLoweringPhase,
|
||||||
privateMembersLoweringPhase,
|
privateMembersLoweringPhase,
|
||||||
privateMemberUsagesLoweringPhase,
|
privateMemberUsagesLoweringPhase,
|
||||||
@@ -763,11 +776,11 @@ val loweringList = listOf<Lowering>(
|
|||||||
secondaryConstructorLoweringPhase,
|
secondaryConstructorLoweringPhase,
|
||||||
secondaryFactoryInjectorLoweringPhase,
|
secondaryFactoryInjectorLoweringPhase,
|
||||||
classReferenceLoweringPhase,
|
classReferenceLoweringPhase,
|
||||||
|
constLoweringPhase,
|
||||||
inlineClassDeclarationLoweringPhase,
|
inlineClassDeclarationLoweringPhase,
|
||||||
inlineClassUsageLoweringPhase,
|
inlineClassUsageLoweringPhase,
|
||||||
autoboxingTransformerPhase,
|
autoboxingTransformerPhase,
|
||||||
blockDecomposerLoweringPhase,
|
blockDecomposerLoweringPhase,
|
||||||
constLoweringPhase,
|
|
||||||
objectDeclarationLoweringPhase,
|
objectDeclarationLoweringPhase,
|
||||||
invokeStaticInitializersPhase,
|
invokeStaticInitializersPhase,
|
||||||
objectUsageLoweringPhase,
|
objectUsageLoweringPhase,
|
||||||
|
|||||||
+37
@@ -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<IrDeclaration>? {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
-32
@@ -161,39 +161,36 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
val function = expression.symbol.owner
|
val function = expression.symbol.owner
|
||||||
val arguments = translateCallArguments(expression, context, this)
|
val arguments = translateCallArguments(expression, context, this)
|
||||||
val klass = function.parentAsClass
|
val klass = function.parentAsClass
|
||||||
return if (klass.isInline) {
|
|
||||||
assert(function.isPrimary) {
|
require(!klass.isInline) {
|
||||||
"Inline class secondary constructors must be lowered into static methods"
|
"All inline class constructor calls must be lowered to static function calls"
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Argument value constructs unboxed inline class instance
|
else -> {
|
||||||
arguments.single()
|
val ref = context.getNameForClass(klass).makeRef()
|
||||||
} else {
|
JsNew(ref, arguments)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// DONT_TARGET_EXACT_BACKEND: WASM
|
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||||
// WASM_MUTE_REASON: IGNORED_IN_JS
|
// WASM_MUTE_REASON: IGNORED_IN_JS
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
|
|
||||||
inline class IC(val i: Int) {
|
inline class IC(val i: Int) {
|
||||||
init {
|
init {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// DONT_TARGET_EXACT_BACKEND: WASM
|
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||||
// WASM_MUTE_REASON: IGNORED_IN_JS
|
// WASM_MUTE_REASON: IGNORED_IN_JS
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
|
|
||||||
inline class SingleInitBlock(val s: String) {
|
inline class SingleInitBlock(val s: String) {
|
||||||
init {
|
init {
|
||||||
|
|||||||
Reference in New Issue
Block a user