[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>? {
|
||||
if (irConstructor.isPrimary) return null
|
||||
private fun transformConstructor(irConstructor: IrConstructor): List<IrDeclaration> {
|
||||
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<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) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Lowering>(
|
||||
propertyLazyInitLoweringPhase,
|
||||
removeInitializersForLazyProperties,
|
||||
propertyAccessorInlinerLoweringPhase,
|
||||
copyPropertyAccessorBodiesLoweringPass,
|
||||
foldConstantLoweringPhase,
|
||||
privateMembersLoweringPhase,
|
||||
privateMemberUsagesLoweringPhase,
|
||||
@@ -763,11 +776,11 @@ val loweringList = listOf<Lowering>(
|
||||
secondaryConstructorLoweringPhase,
|
||||
secondaryFactoryInjectorLoweringPhase,
|
||||
classReferenceLoweringPhase,
|
||||
constLoweringPhase,
|
||||
inlineClassDeclarationLoweringPhase,
|
||||
inlineClassUsageLoweringPhase,
|
||||
autoboxingTransformerPhase,
|
||||
blockDecomposerLoweringPhase,
|
||||
constLoweringPhase,
|
||||
objectDeclarationLoweringPhase,
|
||||
invokeStaticInitializersPhase,
|
||||
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 arguments = translateCallArguments(expression, context, this)
|
||||
val klass = function.parentAsClass
|
||||
return if (klass.isInline) {
|
||||
assert(function.isPrimary) {
|
||||
"Inline class secondary constructors must be lowered into static methods"
|
||||
|
||||
require(!klass.isInline) {
|
||||
"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
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user