[JS IR BE] Inline classes lowering
This commit is contained in:
+284
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrStringConcatenationImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
|
||||
/**
|
||||
* Transforms expressions depending on the context they are used in.
|
||||
*
|
||||
* The transformations are defined with `IrExpression.use*` methods in this class,
|
||||
* the most common are [useAs], [useAsStatement], [useInTypeOperator].
|
||||
*
|
||||
* NOTE: Transformer is copied from Kotlin/Native with minor modifications
|
||||
*
|
||||
* TODO: the implementation is originally based on [org.jetbrains.kotlin.psi2ir.transformations.InsertImplicitCasts]
|
||||
* and should probably be used as its base.
|
||||
*
|
||||
* TODO: consider making this visitor non-recursive to make it more general.
|
||||
*/
|
||||
abstract class AbstractValueUsageTransformer(
|
||||
protected val irBuiltIns: IrBuiltIns
|
||||
) : IrElementTransformerVoid() {
|
||||
|
||||
protected open fun IrExpression.useAs(type: IrType): IrExpression = this
|
||||
|
||||
protected open fun IrExpression.useAsStatement(): IrExpression = this
|
||||
|
||||
protected open fun IrExpression.useInTypeOperator(operator: IrTypeOperator, typeOperand: IrType): IrExpression =
|
||||
this
|
||||
|
||||
protected open fun IrExpression.useAsValue(value: IrValueDeclaration): IrExpression = this.useAs(value.type)
|
||||
|
||||
protected open fun IrExpression.useAsArgument(parameter: IrValueParameter): IrExpression =
|
||||
this.useAsValue(parameter)
|
||||
|
||||
protected open fun IrExpression.useAsDispatchReceiver(expression: IrFunctionAccessExpression): IrExpression =
|
||||
this.useAsArgument(expression.symbol.owner.dispatchReceiverParameter!!)
|
||||
|
||||
protected open fun IrExpression.useAsExtensionReceiver(expression: IrFunctionAccessExpression): IrExpression =
|
||||
this.useAsArgument(expression.symbol.owner.extensionReceiverParameter!!)
|
||||
|
||||
protected open fun IrExpression.useAsValueArgument(
|
||||
expression: IrFunctionAccessExpression,
|
||||
parameter: IrValueParameter
|
||||
): IrExpression =
|
||||
this.useAsArgument(parameter)
|
||||
|
||||
private fun IrExpression.useForVariable(variable: IrVariable): IrExpression =
|
||||
this.useAsValue(variable)
|
||||
|
||||
private fun IrExpression.useForField(field: IrField): IrExpression =
|
||||
this.useAs(field.type)
|
||||
|
||||
protected open fun IrExpression.useAsReturnValue(returnTarget: IrReturnTargetSymbol): IrExpression =
|
||||
when (returnTarget) {
|
||||
is IrSimpleFunctionSymbol -> this.useAs(returnTarget.owner.returnType)
|
||||
is IrConstructorSymbol -> this.useAs(irBuiltIns.unitType)
|
||||
is IrReturnableBlockSymbol -> this.useAs(returnTarget.owner.type)
|
||||
else -> error(returnTarget)
|
||||
}
|
||||
|
||||
protected open fun IrExpression.useAsResult(enclosing: IrExpression): IrExpression =
|
||||
this.useAs(enclosing.type)
|
||||
|
||||
protected open fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression = this
|
||||
|
||||
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression {
|
||||
TODO()
|
||||
}
|
||||
|
||||
// override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
// TODO()
|
||||
// }
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
with(expression) {
|
||||
dispatchReceiver = dispatchReceiver?.useAsDispatchReceiver(expression)
|
||||
extensionReceiver = extensionReceiver?.useAsExtensionReceiver(expression)
|
||||
for (index in descriptor.valueParameters.indices) {
|
||||
val argument = getValueArgument(index) ?: continue
|
||||
val parameter = symbol.owner.valueParameters[index]
|
||||
putValueArgument(index, argument.useAsValueArgument(expression, parameter))
|
||||
}
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitBlockBody(body: IrBlockBody): IrBody {
|
||||
body.transformChildrenVoid(this)
|
||||
|
||||
body.statements.forEachIndexed { i, irStatement ->
|
||||
if (irStatement is IrExpression) {
|
||||
body.statements[i] = irStatement.useAsStatement()
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
if (expression.statements.isEmpty()) {
|
||||
return expression
|
||||
}
|
||||
|
||||
val lastIndex = expression.statements.lastIndex
|
||||
expression.statements.forEachIndexed { i, irStatement ->
|
||||
if (irStatement is IrExpression) {
|
||||
expression.statements[i] =
|
||||
if (i == lastIndex)
|
||||
irStatement.useAsResult(expression)
|
||||
else
|
||||
irStatement.useAsStatement()
|
||||
}
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.value = expression.value.useAsReturnValue(expression.returnTargetSymbol)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.value = expression.value.useForVariable(expression.symbol.owner)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.value = expression.value.useForField(expression.symbol.owner)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
declaration.initializer?.let {
|
||||
it.expression = it.expression.useForField(declaration)
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable): IrVariable {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
declaration.initializer = declaration.initializer?.useForVariable(declaration)
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
for (irBranch in expression.branches) {
|
||||
irBranch.condition = irBranch.condition.useAs(irBuiltIns.booleanType)
|
||||
irBranch.result = irBranch.result.useAsResult(expression)
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitLoop(loop: IrLoop): IrExpression {
|
||||
loop.transformChildrenVoid(this)
|
||||
|
||||
loop.condition = loop.condition.useAs(irBuiltIns.booleanType)
|
||||
|
||||
loop.body = loop.body?.useAsStatement()
|
||||
|
||||
return loop
|
||||
}
|
||||
|
||||
override fun visitThrow(expression: IrThrow): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.value = expression.value.useAs(irBuiltIns.throwableType)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitTry(aTry: IrTry): IrExpression {
|
||||
aTry.transformChildrenVoid(this)
|
||||
|
||||
aTry.tryResult = aTry.tryResult.useAsResult(aTry)
|
||||
|
||||
for (aCatch in aTry.catches) {
|
||||
aCatch.result = aCatch.result.useAsResult(aTry)
|
||||
}
|
||||
|
||||
aTry.finallyExpression = aTry.finallyExpression?.useAsStatement()
|
||||
|
||||
return aTry
|
||||
}
|
||||
|
||||
override fun visitVararg(expression: IrVararg): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.elements.forEachIndexed { i, element ->
|
||||
when (element) {
|
||||
is IrSpreadElement ->
|
||||
element.expression = element.expression.useAs(expression.type)
|
||||
is IrExpression -> {
|
||||
expression.putElement(i, element.useAsVarargElement(expression))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.argument = expression.argument.useInTypeOperator(expression.operator, expression.typeOperand)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
declaration.valueParameters.forEach { parameter ->
|
||||
val defaultValue = parameter.defaultValue
|
||||
if (defaultValue is IrExpressionBody) {
|
||||
defaultValue.expression = defaultValue.expression.useAsArgument(parameter)
|
||||
}
|
||||
}
|
||||
|
||||
declaration.body?.let {
|
||||
if (it is IrExpressionBody) {
|
||||
it.expression = it.expression.useAsReturnValue(declaration.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
if (expression is IrStringConcatenationImpl) {
|
||||
for ((i, arg) in expression.arguments.withIndex()) {
|
||||
expression.arguments[i] = arg.useAs(irBuiltIns.anyNType)
|
||||
}
|
||||
}
|
||||
return expression
|
||||
}
|
||||
|
||||
// TODO: IrEnumEntry?
|
||||
|
||||
}
|
||||
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
private const val INLINE_CLASS_IMPL_SUFFIX = "-impl"
|
||||
|
||||
// TODO: Support incremental compilation
|
||||
class InlineClassLowering(val context: BackendContext) {
|
||||
private val transformedFunction = mutableMapOf<IrFunctionSymbol, IrSimpleFunctionSymbol>()
|
||||
|
||||
val inlineClassDeclarationLowering = object : ClassLoweringPass {
|
||||
override fun lower(irClass: IrClass) {
|
||||
if (!irClass.isInline) return
|
||||
|
||||
irClass.transformDeclarationsFlat { declaration ->
|
||||
when (declaration) {
|
||||
is IrConstructor -> listOf(transformConstructor(declaration))
|
||||
is IrSimpleFunction -> transformMethodFlat(declaration)
|
||||
is IrProperty -> listOf(declaration) // Getters and setters should be flattened
|
||||
is IrField -> listOf(declaration)
|
||||
is IrClass -> listOf(declaration)
|
||||
else -> error("Unexpected declaration: $declaration")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformConstructor(irConstructor: IrConstructor): IrDeclaration {
|
||||
if (irConstructor.isPrimary) return irConstructor
|
||||
|
||||
// Secondary constructors are lowered into static function
|
||||
val result = transformedFunction.getOrPut(irConstructor.symbol) { createStaticBodilessMethod(irConstructor).symbol }.owner
|
||||
val irClass = irConstructor.parentAsClass
|
||||
|
||||
// Copied and adapted from Kotlin/Native InlineClassTransformer
|
||||
result.body = context.createIrBuilder(result.symbol).irBlockBody(result) {
|
||||
|
||||
// Secondary ctors of inline class must delegate to some other constructors.
|
||||
// Use these delegating call later to initialize this variable.
|
||||
lateinit var thisVar: IrVariable
|
||||
val parameterMapping = result.valueParameters.associateBy { it ->
|
||||
irConstructor.valueParameters[it.index].symbol
|
||||
}
|
||||
|
||||
(irConstructor.body as IrBlockBody).statements.forEach { statement ->
|
||||
+statement.transform(object : IrElementTransformerVoid() {
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
return irBlock(expression) {
|
||||
thisVar = irTemporary(
|
||||
expression,
|
||||
typeHint = irClass.defaultType.toKotlinType(),
|
||||
irType = irClass.defaultType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
if (expression.symbol == irClass.thisReceiver?.symbol) {
|
||||
return irGet(thisVar)
|
||||
}
|
||||
|
||||
parameterMapping[expression.symbol]?.let { return irGet(it) }
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
if (expression.returnTargetSymbol == irConstructor.symbol) {
|
||||
return irReturn(irBlock(expression.startOffset, expression.endOffset) {
|
||||
+expression.value
|
||||
+irGet(thisVar)
|
||||
})
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
}, null)
|
||||
}
|
||||
+irReturn(irGet(thisVar))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
private fun transformMethodFlat(function: IrSimpleFunction): List<IrDeclaration> {
|
||||
// TODO: Support fake-overridden methods without boxing
|
||||
if (function.isStaticMethodOfClass || !function.isReal)
|
||||
return listOf(function)
|
||||
|
||||
val staticMethod = createStaticBodilessMethod(function)
|
||||
transformedFunction[function.symbol] = staticMethod.symbol
|
||||
|
||||
// Move function body to static method, transforming value parameters and nested declarations
|
||||
function.body!!.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitDeclaration(declaration: IrDeclaration): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
// TODO: Variable parents might not be initialized
|
||||
if (declaration !is IrVariable && declaration.parent == function)
|
||||
declaration.parent = staticMethod
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
val valueDeclaration = expression.symbol.owner as? IrValueParameter ?: return super.visitGetValue(expression)
|
||||
|
||||
return context.createIrBuilder(staticMethod.symbol).irGet(
|
||||
when (valueDeclaration) {
|
||||
function.dispatchReceiverParameter, function.parentAsClass.thisReceiver ->
|
||||
staticMethod.valueParameters[0]
|
||||
|
||||
function.extensionReceiverParameter ->
|
||||
staticMethod.extensionReceiverParameter!!
|
||||
|
||||
in function.valueParameters ->
|
||||
staticMethod.valueParameters[valueDeclaration.index + 1]
|
||||
|
||||
else -> return expression
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
staticMethod.body = function.body
|
||||
|
||||
if (function.overriddenSymbols.isEmpty()) // Function is used only in unboxed context
|
||||
return listOf(staticMethod)
|
||||
|
||||
// Delegate original function to static implementation
|
||||
function.body = context.createIrBuilder(function.symbol).irBlockBody {
|
||||
+irReturn(
|
||||
irCall(staticMethod).apply {
|
||||
val parameters =
|
||||
listOf(function.dispatchReceiverParameter!!) + function.valueParameters
|
||||
|
||||
for ((index, valueParameter) in parameters.withIndex()) {
|
||||
putValueArgument(index, irGet(valueParameter))
|
||||
}
|
||||
|
||||
extensionReceiver = function.extensionReceiverParameter?.let { irGet(it) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return listOf(function, staticMethod)
|
||||
}
|
||||
}
|
||||
|
||||
val inlineClassUsageLowering = object : FileLoweringPass {
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
|
||||
override fun visitCall(call: IrCall): IrExpression {
|
||||
call.transformChildrenVoid(this)
|
||||
val function = call.symbol.owner
|
||||
if (
|
||||
function.isDynamic() ||
|
||||
function.parent !is IrClass ||
|
||||
function.isStaticMethodOfClass ||
|
||||
!function.parentAsClass.isInline ||
|
||||
(function is IrSimpleFunction && !function.isReal) ||
|
||||
(function is IrConstructor && function.isPrimary)
|
||||
) {
|
||||
return call
|
||||
}
|
||||
|
||||
return irCall(call, getOrCreateStaticMethod(function), dispatchReceiverAsFirstArgument = (function is IrSimpleFunction))
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(call: IrDelegatingConstructorCall): IrExpression {
|
||||
call.transformChildrenVoid(this)
|
||||
val function = call.symbol.owner
|
||||
val klass = function.parentAsClass
|
||||
return when {
|
||||
!klass.isInline -> call
|
||||
function.isPrimary -> irCall(call, function)
|
||||
else -> irCall(call, getOrCreateStaticMethod(function)).apply {
|
||||
(0 until call.valueArgumentsCount).forEach {
|
||||
putValueArgument(it, call.getValueArgument(it)!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOrCreateStaticMethod(function: IrFunction): IrSimpleFunctionSymbol =
|
||||
transformedFunction.getOrPut(function.symbol) {
|
||||
createStaticBodilessMethod(function).also {
|
||||
function.parentAsClass.declarations.add(it)
|
||||
}.symbol
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun Name.toInlineClassImplementationName() = when {
|
||||
isSpecial -> Name.special(asString() + INLINE_CLASS_IMPL_SUFFIX)
|
||||
else -> Name.identifier(asString() + INLINE_CLASS_IMPL_SUFFIX)
|
||||
}
|
||||
|
||||
private fun createStaticBodilessMethod(function: IrFunction): IrSimpleFunction {
|
||||
val descriptor = WrappedSimpleFunctionDescriptor()
|
||||
return IrFunctionImpl(
|
||||
function.startOffset,
|
||||
function.endOffset,
|
||||
function.origin,
|
||||
IrSimpleFunctionSymbolImpl(descriptor),
|
||||
function.name.toInlineClassImplementationName(),
|
||||
function.visibility,
|
||||
Modality.FINAL,
|
||||
function.isInline,
|
||||
function.isExternal,
|
||||
(function is IrSimpleFunction && function.isTailrec),
|
||||
(function is IrSimpleFunction && function.isSuspend)
|
||||
).apply {
|
||||
descriptor.bind(this)
|
||||
returnType = when (function) {
|
||||
is IrSimpleFunction -> function.returnType
|
||||
is IrConstructor -> function.parentAsClass.defaultType
|
||||
else -> error("Unknown function type")
|
||||
}
|
||||
typeParameters += function.typeParameters
|
||||
annotations += function.annotations
|
||||
dispatchReceiverParameter = null
|
||||
extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(this)
|
||||
if (function is IrSimpleFunction) {
|
||||
valueParameters.add(function.dispatchReceiverParameter!!.copyTo(this, shift = 1))
|
||||
valueParameters += function.valueParameters.map { p -> p.copyTo(this, shift = 1) }
|
||||
} else {
|
||||
valueParameters += function.valueParameters.map { p -> p.copyTo(this, shift = 0) }
|
||||
}
|
||||
parent = function.parent
|
||||
assert(isStaticMethodOfClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.util
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.types.isMarkedNullable
|
||||
|
||||
|
||||
/**
|
||||
* Returns inline class for given class or null of type is not inlined
|
||||
* TODO: Make this configurable for different backends (currently implements logic of JS BE)
|
||||
*/
|
||||
fun IrType.getInlinedClass(): IrClass? {
|
||||
if (this is IrSimpleType) {
|
||||
val erased = erase(this) ?: return null
|
||||
if (!this.isMarkedNullable() && erased.isInline) {
|
||||
// TODO: Don't box nullable type inline classes with non-nullable underlying type (JS IR BE)
|
||||
return erased
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun IrType.isInlined(): Boolean = this.getInlinedClass() != null
|
||||
|
||||
private tailrec fun erase(type: IrType): IrClass? {
|
||||
val classifier = type.classifierOrFail
|
||||
|
||||
// TODO: Fix unbound symbols
|
||||
if (!classifier.isBound)
|
||||
return null
|
||||
|
||||
return when (classifier) {
|
||||
is IrClassSymbol -> classifier.owner
|
||||
is IrTypeParameterSymbol -> erase(classifier.owner.superTypes.first())
|
||||
else -> error(classifier)
|
||||
}
|
||||
}
|
||||
|
||||
fun getInlineClassBackingField(irClass: IrClass): IrField {
|
||||
for (declaration in irClass.declarations) {
|
||||
if (declaration is IrField)
|
||||
return declaration
|
||||
|
||||
if (declaration is IrProperty)
|
||||
return declaration.backingField ?: continue
|
||||
}
|
||||
error("Inline class has no field")
|
||||
}
|
||||
@@ -108,4 +108,7 @@ internal fun newThrowable(message: String?, cause: Throwable?): Throwable {
|
||||
throwable.cause = cause
|
||||
throwable.name = "Throwable"
|
||||
return throwable
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T, R> boxIntrinsic(x: T): R = error("Should be lowered")
|
||||
internal fun <T, R> unboxIntrinsic(x: T): R = error("Should be lowered")
|
||||
|
||||
Reference in New Issue
Block a user