JVM_IR: lift pre-super() computations out of anonymous objects
This commit is contained in:
@@ -178,6 +178,7 @@ private val jvmFilePhases =
|
|||||||
propertiesToFieldsPhase then
|
propertiesToFieldsPhase then
|
||||||
propertiesPhase then
|
propertiesPhase then
|
||||||
renameFieldsPhase then
|
renameFieldsPhase then
|
||||||
|
anonymousObjectSuperConstructorPhase then
|
||||||
assertionPhase then
|
assertionPhase then
|
||||||
tailrecPhase then
|
tailrecPhase then
|
||||||
returnableBlocksPhase then
|
returnableBlocksPhase then
|
||||||
|
|||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 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.backend.jvm.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||||
|
import org.jetbrains.kotlin.ir.util.transform
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
|
||||||
|
internal val anonymousObjectSuperConstructorPhase = makeIrFilePhase(
|
||||||
|
::AnonymousObjectSuperConstructorLowering,
|
||||||
|
name = "AnonymousObjectSuperConstructor",
|
||||||
|
description = "Move evaluation of anonymous object super constructor arguments to call site"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transform code like this:
|
||||||
|
//
|
||||||
|
// object : SuperType(complexExpression) {}
|
||||||
|
//
|
||||||
|
// which looks like this in the IR:
|
||||||
|
//
|
||||||
|
// run {
|
||||||
|
// class _anonymous : SuperType(complexExpression) {}
|
||||||
|
// _anonymous()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// into this:
|
||||||
|
//
|
||||||
|
// run {
|
||||||
|
// class _anonymous(arg: T) : SuperType(arg) {}
|
||||||
|
// _anonymous(complexExpression)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The reason for doing such a transformation is the inliner: if the object is declared
|
||||||
|
// in an inline function, `complexExpression` may be a call to a lambda, which will be
|
||||||
|
// inlined into regenerated copies of the object. Unfortunately, if that lambda captures
|
||||||
|
// some values, the inliner does not notice that `this` is not yet initialized, and
|
||||||
|
// attempts to read them from fields, causing a bytecode validation error.
|
||||||
|
//
|
||||||
|
// (TODO fix the inliner instead. Then keep this code for one more version for backwards compatibility.)
|
||||||
|
private class AnonymousObjectSuperConstructorLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
irFile.transformChildrenVoid()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitBlock(expression: IrBlock): IrExpression {
|
||||||
|
if (expression.origin != IrStatementOrigin.OBJECT_LITERAL)
|
||||||
|
return super.visitBlock(expression)
|
||||||
|
|
||||||
|
val objectConstructorCall = expression.statements.last() as? IrConstructorCall
|
||||||
|
?: throw AssertionError("object literal does not end in a constructor call")
|
||||||
|
val objectConstructor = objectConstructorCall.symbol.owner
|
||||||
|
val objectConstructorBody = objectConstructor.body as? IrBlockBody
|
||||||
|
?: throw AssertionError("object literal constructor body is not a block")
|
||||||
|
|
||||||
|
val newArguments = mutableListOf<IrExpression>()
|
||||||
|
fun addArgument(value: IrExpression): IrValueParameter {
|
||||||
|
newArguments.add(value)
|
||||||
|
return objectConstructor.addValueParameter("\$super_call_param\$${newArguments.size}", value.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IrDelegatingConstructorCall.transform(lift: List<IrVariable>) = apply {
|
||||||
|
val remapping = lift.associateWith { addArgument(it.initializer!!) }
|
||||||
|
for (i in 0 until symbol.owner.valueParameters.size) {
|
||||||
|
val argument = getValueArgument(i) ?: continue
|
||||||
|
val mapped = when (argument) {
|
||||||
|
is IrConst<*> -> null
|
||||||
|
is IrGetValue -> remapping[argument.symbol.owner]
|
||||||
|
else -> addArgument(argument)
|
||||||
|
} ?: continue
|
||||||
|
putValueArgument(i, IrGetValueImpl(argument.startOffset, argument.endOffset, mapped.symbol))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
objectConstructorBody.statements.transform {
|
||||||
|
when {
|
||||||
|
it is IrDelegatingConstructorCall -> it.transform(listOf())
|
||||||
|
it is IrBlock && it.origin == IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL && it.statements.last() is IrDelegatingConstructorCall ->
|
||||||
|
// If named arguments are used, the order of evaluation may not match the order of arguments,
|
||||||
|
// in which case IR like this is generated:
|
||||||
|
//
|
||||||
|
// val _tmp1 = complexExpression1
|
||||||
|
// val _tmp2 = complexExpression2
|
||||||
|
// SuperType(_tmp2, _tmp1)
|
||||||
|
//
|
||||||
|
// So we lift all temporary variables to parameters.
|
||||||
|
(it.statements.last() as IrDelegatingConstructorCall).transform(it.statements.filterIsInstance<IrVariable>())
|
||||||
|
else -> it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expression.statements[expression.statements.size - 1] = IrConstructorCallImpl.fromSymbolOwner(
|
||||||
|
objectConstructorCall.startOffset, objectConstructorCall.endOffset, objectConstructorCall.type,
|
||||||
|
objectConstructorCall.symbol, objectConstructorCall.origin
|
||||||
|
).apply {
|
||||||
|
for (i in 0 until objectConstructorCall.valueArgumentsCount)
|
||||||
|
putValueArgument(i, objectConstructorCall.getValueArgument(i))
|
||||||
|
for ((i, argument) in newArguments.withIndex())
|
||||||
|
putValueArgument(i + objectConstructorCall.valueArgumentsCount, argument)
|
||||||
|
}
|
||||||
|
return super.visitBlock(expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user