[JS IR] Support init blocks in inline classes

This commit is contained in:
Svyatoslav Kuzmich
2020-12-20 19:02:07 +03:00
parent 07b6f0d871
commit 0945c110bf
6 changed files with 157 additions and 46 deletions
@@ -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,
@@ -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
}
}
@@ -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 {