[JS IR BE] Inline classes lowering
This commit is contained in:
@@ -250,6 +250,9 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
||||
val jsCharSequenceLength = getInternalFunction("charSequenceLength")
|
||||
val jsCharSequenceSubSequence = getInternalFunction("charSequenceSubSequence")
|
||||
|
||||
val jsBoxIntrinsic = getInternalFunction("boxIntrinsic")
|
||||
val jsUnboxIntrinsic = getInternalFunction("unboxIntrinsic")
|
||||
|
||||
// Helpers:
|
||||
|
||||
private fun getInternalFunction(name: String) =
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.*
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.common.lower.InlineClassLowering
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
@@ -130,6 +131,11 @@ private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependenc
|
||||
constructorRedirectorLowering.runOnFilesPostfix(moduleFragment)
|
||||
}
|
||||
|
||||
InlineClassLowering(this).apply {
|
||||
inlineClassDeclarationLowering.runOnFilesPostfix(moduleFragment)
|
||||
inlineClassUsageLowering.lower(moduleFragment)
|
||||
}
|
||||
AutoboxingTransformer(this).lower(moduleFragment)
|
||||
|
||||
ClassReferenceLowering(this).lower(moduleFragment)
|
||||
PrimitiveCompanionLowering(this).lower(moduleFragment)
|
||||
@@ -152,4 +158,4 @@ private fun FunctionLoweringPass.runOnFilesPostfix(moduleFragment: IrModuleFragm
|
||||
moduleFragment.files.forEach { runOnFilePostfix(it) }
|
||||
|
||||
private fun ClassLoweringPass.runOnFilesPostfix(moduleFragment: IrModuleFragment) =
|
||||
moduleFragment.files.forEach { runOnFilePostfix(it) }
|
||||
moduleFragment.files.forEach { runOnFilePostfix(it) }
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.AbstractValueUsageTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.isNothing
|
||||
import org.jetbrains.kotlin.ir.types.makeNotNull
|
||||
import org.jetbrains.kotlin.ir.util.getInlinedClass
|
||||
import org.jetbrains.kotlin.ir.util.isInlined
|
||||
|
||||
|
||||
// Copied and adapted from Kotlin/Native
|
||||
|
||||
class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsageTransformer(context.irBuiltIns), FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid()
|
||||
}
|
||||
|
||||
override fun IrExpression.useAs(type: IrType): IrExpression {
|
||||
|
||||
val actualType = when (this) {
|
||||
is IrCall -> {
|
||||
if (this.symbol.owner.let { it is IrSimpleFunction && it.isSuspend }) {
|
||||
irBuiltIns.anyNType
|
||||
} else {
|
||||
try {
|
||||
this.symbol.owner.returnType
|
||||
} catch (e: kotlin.UninitializedPropertyAccessException) {
|
||||
// TODO: Fix lateinit return types
|
||||
this.type
|
||||
}
|
||||
}
|
||||
}
|
||||
is IrGetField -> this.symbol.owner.type
|
||||
|
||||
is IrTypeOperatorCall -> when (this.operator) {
|
||||
IrTypeOperator.IMPLICIT_INTEGER_COERCION ->
|
||||
// TODO: is it a workaround for inconsistent IR?
|
||||
this.typeOperand
|
||||
|
||||
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> context.irBuiltIns.anyNType
|
||||
|
||||
else -> this.type
|
||||
}
|
||||
|
||||
is IrGetValue -> {
|
||||
val value = this.symbol.owner
|
||||
if (value is IrValueParameter && value.isDispatchReceiver) {
|
||||
irBuiltIns.anyNType
|
||||
} else {
|
||||
this.type
|
||||
}
|
||||
}
|
||||
|
||||
else -> this.type
|
||||
}
|
||||
|
||||
// TODO: Default parameters are passed as nulls and they need not to be unboxed. Fix this
|
||||
if (actualType.makeNotNull().isNothing())
|
||||
return this
|
||||
|
||||
val expectedType = type
|
||||
|
||||
val actualInlinedClass = actualType.getInlinedClass()
|
||||
val expectedInlinedClass = expectedType.getInlinedClass()
|
||||
|
||||
val function = when {
|
||||
actualInlinedClass == null && expectedInlinedClass == null -> return this
|
||||
actualInlinedClass != null && expectedInlinedClass == null -> context.intrinsics.jsBoxIntrinsic
|
||||
actualInlinedClass == null && expectedInlinedClass != null -> context.intrinsics.jsUnboxIntrinsic
|
||||
else -> return this
|
||||
}
|
||||
|
||||
return JsIrBuilder.buildCall(
|
||||
function,
|
||||
expectedType,
|
||||
typeArguments = listOf(actualType, expectedType)
|
||||
).also {
|
||||
it.putValueArgument(0, this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression {
|
||||
return this.useAs(
|
||||
if (this.type.isInlined())
|
||||
irBuiltIns.anyNType
|
||||
else
|
||||
expression.varargElementType
|
||||
)
|
||||
}
|
||||
|
||||
private val IrValueParameter.isDispatchReceiver: Boolean
|
||||
get() {
|
||||
val parent = this.parent
|
||||
if (parent is IrClass)
|
||||
return true
|
||||
if (parent is IrFunction && parent.dispatchReceiverParameter == this)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
+12
-5
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeProjection
|
||||
import org.jetbrains.kotlin.ir.types.toIrType
|
||||
import org.jetbrains.kotlin.ir.util.isInlined
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
@@ -357,6 +358,12 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
|
||||
return returnStatements
|
||||
}
|
||||
|
||||
private fun IrType.boxIfInlined() = if (isInlined()) {
|
||||
context.irBuiltIns.anyNType
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
private fun generateSignatureForClosure(
|
||||
callable: IrFunction,
|
||||
factory: IrFunction,
|
||||
@@ -387,7 +394,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
|
||||
|
||||
callable.dispatchReceiverParameter?.let { dispatch ->
|
||||
if (reference.dispatchReceiver == null) {
|
||||
result.add(JsIrBuilder.buildValueParameter(dispatch.name, result.size, dispatch.type).also { it.parent = closure })
|
||||
result.add(JsIrBuilder.buildValueParameter(dispatch.name, result.size, dispatch.type.boxIfInlined()).also { it.parent = closure })
|
||||
} else {
|
||||
// do not add dispatch receiver in result signature if it is bound
|
||||
capturedParams--
|
||||
@@ -396,7 +403,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
|
||||
|
||||
callable.extensionReceiverParameter?.let { ext ->
|
||||
if (reference.extensionReceiver == null) {
|
||||
result.add(JsIrBuilder.buildValueParameter(ext.name, result.size, ext.type).also { it.parent = closure })
|
||||
result.add(JsIrBuilder.buildValueParameter(ext.name, result.size, ext.type.boxIfInlined()).also { it.parent = closure })
|
||||
} else {
|
||||
// the same as for dispatch
|
||||
capturedParams--
|
||||
@@ -406,7 +413,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
|
||||
for ((index, param) in (result.size until arity).zip(callable.valueParameters.drop(capturedParams))) {
|
||||
val type = if (index < functionSignature.size) functionSignature[index] else param.type
|
||||
val paramName = param.name.run { if (!isSpecial) identifier else "p$index" }
|
||||
result += JsIrBuilder.buildValueParameter(paramName, result.size, type).also { it.parent = closure }
|
||||
result += JsIrBuilder.buildValueParameter(paramName, result.size, type.boxIfInlined()).also { it.parent = closure }
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -492,7 +499,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
|
||||
val boundParamSymbols = factoryFunction.valueParameters.map { it.symbol }
|
||||
val unboundParamDeclarations = generateSignatureForClosure(declaration, factoryFunction, closureFunction, reference, arity)
|
||||
val unboundParamSymbols = unboundParamDeclarations.map { it.symbol }
|
||||
val returnType = declaration.returnType
|
||||
val returnType = declaration.returnType.boxIfInlined()
|
||||
|
||||
closureFunction.valueParameters += unboundParamDeclarations
|
||||
closureFunction.returnType = returnType
|
||||
@@ -540,4 +547,4 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
|
||||
|
||||
return closureFunction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -38,14 +38,18 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) {
|
||||
|
||||
val constructorProcessorLowering = object : DeclarationContainerLoweringPass {
|
||||
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
||||
irDeclarationContainer.declarations.filterIsInstance<IrClass>().forEach { lowerClass(it) }
|
||||
irDeclarationContainer.declarations.filterIsInstance<IrClass>().forEach {
|
||||
if (!it.isInline) // Inline classes are lowered separately
|
||||
lowerClass(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val constructorRedirectorLowering = object : DeclarationContainerLoweringPass {
|
||||
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
||||
if (irDeclarationContainer is IrClass) {
|
||||
updateConstructorDeclarations(irDeclarationContainer)
|
||||
if (!irDeclarationContainer.isInline) // Inline classes are lowered separately
|
||||
updateConstructorDeclarations(irDeclarationContainer)
|
||||
}
|
||||
for (it in irDeclarationContainer.declarations) {
|
||||
it.accept(CallsiteRedirectionTransformer(), null)
|
||||
|
||||
+31
-2
@@ -8,12 +8,14 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrDynamicType
|
||||
import org.jetbrains.kotlin.ir.util.isFunctionTypeOrSubtype
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
@@ -63,6 +65,12 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
}
|
||||
|
||||
override fun visitGetField(expression: IrGetField, context: JsGenerationContext): JsExpression {
|
||||
if (expression.symbol.isBound) {
|
||||
val fieldParent = expression.symbol.owner.parent
|
||||
if (fieldParent is IrClass && fieldParent.isInline) {
|
||||
return expression.receiver!!.accept(this, context)
|
||||
}
|
||||
}
|
||||
val fieldName = context.getNameForSymbol(expression.symbol)
|
||||
return JsNameRef(fieldName, expression.receiver?.accept(this, context))
|
||||
}
|
||||
@@ -100,6 +108,15 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
val thisRef =
|
||||
if (fromPrimary) JsThisRef() else context.getNameForSymbol(context.currentFunction!!.valueParameters.last().symbol).makeRef()
|
||||
val arguments = translateCallArguments(expression, context)
|
||||
|
||||
val constructor = expression.symbol.owner
|
||||
if (constructor.parentAsClass.isInline) {
|
||||
assert(constructor.isPrimary) {
|
||||
"Delegation to secondary inline constructors must be lowered into simple function calls"
|
||||
}
|
||||
return JsBinaryOperation(JsBinaryOperator.ASG, thisRef, arguments.single())
|
||||
}
|
||||
|
||||
return JsInvocation(callFuncRef, listOf(thisRef) + arguments)
|
||||
}
|
||||
|
||||
@@ -127,7 +144,19 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
}
|
||||
|
||||
return if (symbol is IrConstructorSymbol) {
|
||||
JsNew(context.getNameForSymbol(symbol).makeRef(), arguments)
|
||||
// Inline class primary constructor takes a single value of to
|
||||
// initialize underlying property.
|
||||
// TODO: Support initialization block
|
||||
val klass = symbol.owner.parentAsClass
|
||||
if (klass.isInline) {
|
||||
assert(symbol.owner.isPrimary) {
|
||||
"Inline class secondary constructors must be lowered into static methods"
|
||||
}
|
||||
// Argument value constructs unboxed inline class instance
|
||||
arguments.single()
|
||||
} else {
|
||||
JsNew(context.getNameForSymbol(symbol).makeRef(), arguments)
|
||||
}
|
||||
} else {
|
||||
val symbolName = context.getNameForSymbol(symbol)
|
||||
val ref = if (jsDispatchReceiver != null) JsNameRef(symbolName, jsDispatchReceiver) else JsNameRef(symbolName)
|
||||
@@ -157,4 +186,4 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
|
||||
return simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunctionTypeOrSubtype()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -8,10 +8,13 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
|
||||
import org.jetbrains.kotlin.ir.util.getInlinedClass
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
typealias IrCallTransformer = (IrCall, context: JsGenerationContext) -> JsExpression
|
||||
@@ -183,6 +186,21 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
|
||||
JsNew(JsNameRef("${prefix}Array"), translateCallArguments(call, context))
|
||||
}
|
||||
}
|
||||
|
||||
add(intrinsics.jsBoxIntrinsic) { call: IrCall, context ->
|
||||
val arg = translateCallArguments(call, context).single()
|
||||
val inlineClass = call.getTypeArgument(0)!!.getInlinedClass()!!
|
||||
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
|
||||
JsNew(context.getNameForSymbol(constructor.symbol).makeRef(), listOf(arg))
|
||||
}
|
||||
|
||||
add(intrinsics.jsUnboxIntrinsic) { call: IrCall, context ->
|
||||
val arg = translateCallArguments(call, context).single()
|
||||
val inlineClass = call.getTypeArgument(1)!!.getInlinedClass()!!
|
||||
val field = getInlineClassBackingField(inlineClass)
|
||||
val fieldName = context.getNameForSymbol(field.symbol)
|
||||
JsNameRef(fieldName, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user