diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index 73b854216dd..c0b3f6968af 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -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() + + 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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index e8bba0c9bc9..47061880fb8 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -95,6 +95,7 @@ val jvmPhases = namedIrFilePhase( description = "IR lowering", lower = expectDeclarationsRemovingPhase then fileClassPhase then + inventNamesForLocalClassesPhase then kCallableNamePropertyPhase then arrayConstructorPhase then @@ -154,6 +155,8 @@ val jvmPhases = namedIrFilePhase( jvmBuiltinOptimizationLoweringPhase then additionalClassAnnotationPhase then + recordNamesForKotlinTypeMapperPhase then + // should be last transformation removeDeclarationsThatWouldBeInlined then makePatchParentsPhase(3) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt index f4e5387be59..6277cd8e963 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt @@ -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 { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InventNamesForLocalClasses.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InventNamesForLocalClasses.kt new file mode 100644 index 00000000000..28ef3cca083 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InventNamesForLocalClasses.kt @@ -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( + { 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 { + private val anonymousClassesCount = mutableMapOf() + private val localFunctionNames = mutableMapOf() + + 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 "" 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) + } + } +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt index 937b2ecd5f4..bc426c85843 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt @@ -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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RecordNamesForKotlinTypeMapper.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RecordNamesForKotlinTypeMapper.kt new file mode 100644 index 00000000000..44e186ca8e8 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/RecordNamesForKotlinTypeMapper.kt @@ -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( + { 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) + } +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationContainer.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationContainer.kt index f10cb6ddaf8..3ae16bb58db 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationContainer.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationContainer.kt @@ -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 diff --git a/compiler/testData/checkLocalVariablesTable/lambdaAsVar.kt b/compiler/testData/checkLocalVariablesTable/lambdaAsVar.kt index 923ff0bd8c2..e562031bbd9 100644 --- a/compiler/testData/checkLocalVariablesTable/lambdaAsVar.kt +++ b/compiler/testData/checkLocalVariablesTable/lambdaAsVar.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun foo() { var a = { diff --git a/compiler/testData/writeFlags/callableReference/visibility/functionReferenceInInlineFunction.kt b/compiler/testData/writeFlags/callableReference/visibility/functionReferenceInInlineFunction.kt index 4b6befe7fa4..5c706fde3f7 100644 --- a/compiler/testData/writeFlags/callableReference/visibility/functionReferenceInInlineFunction.kt +++ b/compiler/testData/writeFlags/callableReference/visibility/functionReferenceInInlineFunction.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR class A { fun foo() {} diff --git a/compiler/testData/writeFlags/callableReference/visibility/propertyReferenceInInlineFunction.kt b/compiler/testData/writeFlags/callableReference/visibility/propertyReferenceInInlineFunction.kt index ac1daefb15b..e61f47a5710 100644 --- a/compiler/testData/writeFlags/callableReference/visibility/propertyReferenceInInlineFunction.kt +++ b/compiler/testData/writeFlags/callableReference/visibility/propertyReferenceInInlineFunction.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR class A { val foo = ""