[JS IR BE] Inline classes lowering

This commit is contained in:
Svyatoslav Kuzmich
2018-10-31 17:39:02 +03:00
parent 4866c2b36a
commit 370b04df35
11 changed files with 798 additions and 11 deletions
@@ -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?
}
@@ -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)
}
}
}