JVM IR: add InventNamesForLocalClasses pass

This commit is contained in:
Alexander Udalov
2019-06-14 16:13:42 +02:00
parent 529efff8cb
commit 29fa1793d3
10 changed files with 266 additions and 18 deletions
@@ -12,9 +12,7 @@ import org.jetbrains.kotlin.backend.jvm.descriptors.JvmDeclarationFactory
import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.coroutines.coroutinesJvmInternalPackageFqName
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.coroutinesPackageFqName
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
@@ -24,8 +22,6 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
class JvmBackendContext(
@@ -46,6 +42,18 @@ class JvmBackendContext(
val irIntrinsics = IrIntrinsicMethods(irBuiltIns, ir.symbols)
// TODO: also store info for EnclosingMethod
internal class LocalClassInfo(val internalName: String)
private val localClassInfo = mutableMapOf<IrAttributeContainer, LocalClassInfo>()
internal fun getLocalClassInfo(container: IrAttributeContainer): LocalClassInfo? =
localClassInfo[container.attributeOwnerId]
internal fun putLocalClassInfo(container: IrAttributeContainer, value: LocalClassInfo) {
localClassInfo[container.attributeOwnerId] = value
}
override var inVerbosePhase: Boolean = false
override val configuration get() = state.configuration
@@ -95,6 +95,7 @@ val jvmPhases = namedIrFilePhase<JvmBackendContext>(
description = "IR lowering",
lower = expectDeclarationsRemovingPhase then
fileClassPhase then
inventNamesForLocalClassesPhase then
kCallableNamePropertyPhase then
arrayConstructorPhase then
@@ -154,6 +155,8 @@ val jvmPhases = namedIrFilePhase<JvmBackendContext>(
jvmBuiltinOptimizationLoweringPhase then
additionalClassAnnotationPhase then
recordNamesForKotlinTypeMapperPhase then
// should be last transformation
removeDeclarationsThatWouldBeInlined then
makePatchParentsPhase(3)
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.backend.common.descriptors.isFunctionOrKFunctionType
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.lower.irIfThen
@@ -31,15 +30,22 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.codegen.PropertyReferenceCodegen
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.builders.declarations.addField
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -193,6 +199,7 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
parent = referenceParent
superTypes += functionReferenceOrLambda.owner.defaultType
createImplicitParameterDeclarationWithWrappedDescriptor()
copyAttributes(irFunctionReference)
}
private val argumentToFieldMap = boundCalleeParameters.associate {
@@ -0,0 +1,182 @@
/*
* Copyright 2010-2019 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.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.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.isAnonymousObject
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
val inventNamesForLocalClassesPhase = makeIrFilePhase<JvmBackendContext>(
{ context -> InventNamesForLocalClasses(context) },
name = "InventNamesForLocalClasses",
description = "Invent names for local classes and anonymous objects"
)
class InventNamesForLocalClasses(private val context: JvmBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.accept(NameInventor(), Data(null, false))
}
/**
* @property enclosingName JVM internal name of the enclosing class (including anonymous classes, local objects and callable references)
* @property isLocal true if the next declaration to be encountered in the IR tree is local
*/
private class Data(val enclosingName: String?, val isLocal: Boolean) {
fun withName(newName: String): Data =
Data(newName, isLocal)
fun makeLocal(): Data =
if (isLocal) this else Data(enclosingName, true)
}
private inner class NameInventor : IrElementVisitor<Unit, Data> {
private val anonymousClassesCount = mutableMapOf<String, Int>()
private val localFunctionNames = mutableMapOf<IrFunctionSymbol, String>()
override fun visitClass(declaration: IrClass, data: Data) {
if (!data.isLocal) {
// This is not a local class, so we need not invent a name for it, the type mapper will correctly compute it
// by navigating through its containers.
val internalName = data.enclosingName?.let { enclosingName ->
"$enclosingName$${declaration.name.asString()}"
} ?: (declaration.parent as IrFile).let { file ->
JvmClassName.byFqNameWithoutInnerClasses(file.fqName.child(declaration.name)).internalName
}
declaration.acceptChildren(this, data.withName(internalName))
return
}
val internalName = inventName(declaration.name, data)
context.putLocalClassInfo(declaration, JvmBackendContext.LocalClassInfo(internalName))
val newData = data.withName(internalName)
// Old backend doesn't add the anonymous object name to the stack when traversing its super constructor arguments.
// E.g. a lambda in the super call of an object literal "foo$1" will get the name "foo$2", not "foo$1$1".
val newDataForConstructor =
if (declaration.isAnonymousObject) data else newData
for (child in declaration.declarations) {
child.accept(this, if (child is IrConstructor) newDataForConstructor else newData)
}
}
override fun visitConstructor(declaration: IrConstructor, data: Data) {
// Constructor is a special case because its name "<init>" doesn't participate when creating names for local classes inside.
declaration.acceptChildren(this, data.makeLocal())
}
override fun visitDeclaration(declaration: IrDeclaration, data: Data) {
if (declaration !is IrDeclarationWithName) {
declaration.acceptChildren(this, data)
return
}
val enclosingName = data.enclosingName
val simpleName = declaration.name.asString()
val internalName = when {
declaration.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA -> {
inventName(null, data).also { name ->
// We save the name of the lambda to reuse it in the reference to it (produced by the closure conversion) later.
localFunctionNames[(declaration as IrFunction).symbol] = name
}
}
enclosingName != null -> "$enclosingName$$simpleName"
else -> simpleName
}
val newData = data.withName(internalName).makeLocal()
if (declaration is IrProperty && declaration.isDelegated) {
// Old backend currently reserves a name here, in case a property reference-like anonymous object will need
// to be generated in the codegen later, which is now happening for local delegated properties in inline functions.
// See CodegenAnnotatingVisitor.visitProperty and ExpressionCodegen.initializePropertyMetadata.
inventName(null, newData)
}
declaration.acceptChildren(this, newData)
}
override fun visitFunctionReference(expression: IrFunctionReference, data: Data) {
val internalName = localFunctionNames[expression.symbol] ?: inventName(null, data)
context.putLocalClassInfo(expression, JvmBackendContext.LocalClassInfo(internalName))
expression.acceptChildren(this, data)
}
override fun visitPropertyReference(expression: IrPropertyReference, data: Data) {
val internalName = inventName(null, data)
context.putLocalClassInfo(expression, JvmBackendContext.LocalClassInfo(internalName))
expression.acceptChildren(this, data)
}
override fun visitEnumEntry(declaration: IrEnumEntry, data: Data) {
// Although IrEnumEntry is an IrDeclaration, its name shouldn't be added to nameStack. This is because each IrEnumEntry has
// an IrClass with the same name underneath it, and that class should obtain the name of the form "Enum$Entry",
// not "Enum$Entry$Entry".
declaration.acceptChildren(this, data.makeLocal())
}
override fun visitValueParameter(declaration: IrValueParameter, data: Data) {
// We skip value parameters when constructing names to replicate behavior of the old backend, but this can be safely changed.
declaration.acceptChildren(this, data.makeLocal())
}
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: Data) {
if (declaration.correspondingPropertySymbol != null) {
// Skip adding property accessors to the name stack because the name of the property (which is a parent) is already there.
declaration.acceptChildren(this, data.makeLocal())
return
}
super.visitSimpleFunction(declaration, data)
}
override fun visitField(declaration: IrField, data: Data) {
// Skip field name because the name of the property is already there.
declaration.acceptChildren(this, data.makeLocal())
}
override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer, data: Data) {
// IrAnonymousInitializer is not an IrDeclaration, so we need to manually make all its children aware that they're local
// and might need new invented names.
declaration.acceptChildren(this, data.makeLocal())
}
override fun visitElement(element: IrElement, data: Data) {
element.acceptChildren(this, data)
}
private fun inventName(sourceName: Name?, data: Data): String {
val enclosingName = data.enclosingName
check(enclosingName != null) { "There should be at least one name in the stack for every local declaration that needs a name" }
val simpleName = if (sourceName == null || sourceName.isSpecial) {
val count = (anonymousClassesCount[enclosingName] ?: 0) + 1
anonymousClassesCount[enclosingName] = count
count.toString()
} else {
sourceName
}
return JvmCodegenUtil.sanitizeNameIfNeeded("$enclosingName$$simpleName", context.state.languageVersionSettings)
}
}
}
@@ -16,12 +16,17 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
@@ -194,7 +199,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression =
cachedKProperty(expression)
private fun cachedKProperty(expression: IrMemberAccessExpression): IrExpression {
private fun cachedKProperty(expression: IrCallableReference): IrExpression {
// Reflected implementation does not support partial application; also, cannot cache instances with arguments.
if (expression.dispatchReceiver != null || expression.extensionReceiver != null)
return createSpecializedKProperty(expression)
@@ -220,7 +225,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
// Create an instance of KProperty that uses Java reflection to locate the getter and the setter. This kind of reference
// does not support local variables or bound receivers (e.g. `Class()::field`) and is slower, but takes up less space.
// Example: `C::property` -> `Reflection.property1(PropertyReference1Impl(C::class, "property", "getProperty()LType;"))`.
private fun createReflectedKProperty(expression: IrMemberAccessExpression): IrExpression {
private fun createReflectedKProperty(expression: IrCallableReference): IrExpression {
val referenceKind = propertyReferenceKindFor(expression)
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run {
irCall(referenceKind.wrapper).apply {
@@ -247,7 +252,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
//
// and then `C()::property` -> `C$property$0(C())`.
//
private fun createSpecializedKProperty(expression: IrMemberAccessExpression): IrExpression {
private fun createSpecializedKProperty(expression: IrCallableReference): IrExpression {
val bound = expression.dispatchReceiver != null || expression.extensionReceiver != null
val referenceClass = kPropertyClasses.getOrPut(PropertyClassCacheKey(expression.symbol, bound)) {
createKPropertySubclass(expression)
@@ -261,7 +266,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
}
}
private fun createKPropertySubclass(expression: IrMemberAccessExpression): IrClass {
private fun createKPropertySubclass(expression: IrCallableReference): IrClass {
val superClass = propertyReferenceKindFor(expression).interfaceSymbol.owner
val referenceClass = buildClass {
setSourceRange(expression)
@@ -271,7 +276,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
parent = irClass
superTypes += IrSimpleTypeImpl(superClass.symbol, false, listOf(), listOf())
createImplicitParameterDeclarationWithWrappedDescriptor()
}
}.copyAttributes(expression)
// See propertyReferenceKindFor -- only one of them could ever be present.
val numOfSuperArgs = if (expression.dispatchReceiver != null || expression.extensionReceiver != null) 1 else 0
@@ -0,0 +1,44 @@
/*
* 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.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.org.objectweb.asm.Type
val recordNamesForKotlinTypeMapperPhase = makeIrFilePhase<JvmBackendContext>(
{ context -> RecordNamesForKotlinTypeMapper(context) },
name = "RecordNamesForKotlinTypeMapper",
description = "Record local class and anonymous object names for KotlinTypeMapper to work correctly"
)
class RecordNamesForKotlinTypeMapper(private val context: JvmBackendContext) : FileLoweringPass, IrElementVisitorVoid {
override fun lower(irFile: IrFile) {
irFile.acceptVoid(this)
}
override fun visitClass(declaration: IrClass) {
val internalName = context.getLocalClassInfo(declaration)?.internalName
if (internalName != null) {
// If this line fails, it means that the name invented by the JVM IR backend in InventNamesForLocalClasses is not equal
// to the name invented by the old backend in CodegenAnnotatingVisitor. The former should likely be fixed.
context.state.bindingTrace.record(CodegenBinding.ASM_TYPE, declaration.symbol.descriptor, Type.getObjectType(internalName))
}
super.visitClass(declaration)
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
}
@@ -16,7 +16,9 @@
package org.jetbrains.kotlin.ir.declarations
interface IrDeclarationParent
import org.jetbrains.kotlin.ir.IrElement
interface IrDeclarationParent : IrElement
interface IrDeclarationContainer : IrDeclarationParent {
val declarations: MutableList<IrDeclaration>
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
fun foo() {
var a = {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class A {
fun foo() {}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class A {
val foo = ""