From 247cbffbac871454fa5e72497b61c4af9205344c Mon Sep 17 00:00:00 2001 From: Kristoffer Andersen Date: Fri, 13 Aug 2021 14:51:26 +0200 Subject: [PATCH] [EE-IR] Fragment Compiler Entrypoint in IR Backend This commit introduces a new entrypoint for the IR backend, and starts the work of accomodating the evaluator in psi2ir. The evaluator expects a certain class and method structure of the compiled fragment, but PSI is only supplied for the actual fragment. So, to that end, this commit introduces a new "front end" of psi2ir that "synthesizes" the module, class and method structure around the fragment before calling into the existing psi2ir pipeline to obtain the IR for the fragment. The primary complication so far is handling the captured variables of the fragment: they are mapped to parameters of the method surrounding the fragment, and passed as arguments on evaluation. Hence, the IR translation of the fragment needs to remap captured variables to the appropriate parameter, in essentially all places they can be referred to in the fragment (and hence in psi2ir). This commit introduces a decorated symbol table that intercepts symbol look-ups and remaps as appropriate. Other cases that dispatches based on descriptor (see `CallGenerator.kt`) needs other metadata to generate correct code. It also introduces a shim in DeserializedContainerSource in the psi2ir pipeline to facilitate facade class generation for the code that is being debugged (which are generated as "external ir declarations"). Finally, in passing we resolve a small leftover from previous refactoring that left an asssertion re. allowing IR to _assign_ to parameters of methods. --- ...deClassSourceShimForFragmentCompilation.kt | 43 ++++++ .../backend/jvm/JvmGeneratorExtensionsImpl.kt | 9 +- .../kotlin/backend/jvm/JvmIrCodegenFactory.kt | 27 +++- .../kotlin/psi2ir/Psi2IrTranslator.kt | 15 +- .../kotlin/psi2ir/generators/CallGenerator.kt | 13 +- .../psi2ir/generators/GeneratorContext.kt | 15 +- .../psi2ir/generators/ModuleGenerator.kt | 4 +- .../fragments/EvaluatorFragmentInfo.kt | 26 ++++ .../FragmentCompilerSymbolTableDecorator.kt | 62 +++++++++ .../generators/fragments/FragmentContext.kt | 13 ++ .../fragments/FragmentDeclarationGenerator.kt | 129 ++++++++++++++++++ .../fragments/FragmentModuleGenerator.kt | 47 +++++++ .../psi2ir/intermediate/VariableLValue.kt | 2 +- .../ir/util/DeclarationStubGenerator.kt | 2 +- .../kotlin/ir/util/StubGeneratorExtensions.kt | 9 ++ .../jetbrains/kotlin/ir/util/SymbolTable.kt | 7 +- .../DescriptorByIdSignatureFinder.kt | 4 +- .../kotlin/load/kotlin/FacadeClassSource.kt | 13 ++ .../load/kotlin/JvmPackagePartSource.kt | 6 +- 19 files changed, 421 insertions(+), 25 deletions(-) create mode 100644 compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/FacadeClassSourceShimForFragmentCompilation.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/EvaluatorFragmentInfo.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentCompilerSymbolTableDecorator.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentContext.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentDeclarationGenerator.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentModuleGenerator.kt create mode 100644 core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/FacadeClassSource.kt diff --git a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/FacadeClassSourceShimForFragmentCompilation.kt b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/FacadeClassSourceShimForFragmentCompilation.kt new file mode 100644 index 00000000000..58314ba3f23 --- /dev/null +++ b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/FacadeClassSourceShimForFragmentCompilation.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2010-2021 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 + +import org.jetbrains.kotlin.descriptors.SourceFile +import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo +import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil.getFileClassInfoNoResolve +import org.jetbrains.kotlin.load.kotlin.FacadeClassSource +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.resolve.source.PsiSourceFile +import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerAbiStability +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource + +// Used from CodeFragmentCompiler for IDE Debugger Plug-In +@Suppress("unused") +class FacadeClassSourceShimForFragmentCompilation(private val containingFile: PsiSourceFile) : + DeserializedContainerSource, FacadeClassSource { + + private val fileClassInfo = getFileClassInfoNoResolve(containingFile.psiFile as KtFile) + + override val incompatibility: IncompatibleVersionErrorData<*>? + get() = null + override val isPreReleaseInvisible: Boolean + get() = false + override val abiStability: DeserializedContainerAbiStability + get() = DeserializedContainerAbiStability.STABLE + override val presentableString: String + get() = "Fragment for $containingFile" + + override fun getContainingFile(): SourceFile { + return containingFile + } + + override val className: JvmClassName + get() = JvmClassName.byFqNameWithoutInnerClasses(fileClassInfo.fileClassFqName) + override val facadeClassName: JvmClassName? + get() = JvmClassName.byFqNameWithoutInnerClasses(fileClassInfo.facadeClassFqName) +} diff --git a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmGeneratorExtensionsImpl.kt b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmGeneratorExtensionsImpl.kt index 2eacd58927a..c216d3ab1c6 100644 --- a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmGeneratorExtensionsImpl.kt +++ b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmGeneratorExtensionsImpl.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.load.java.descriptors.getParentJavaStaticClassScope import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability +import org.jetbrains.kotlin.load.kotlin.FacadeClassSource import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement import org.jetbrains.kotlin.name.FqName @@ -55,10 +56,12 @@ import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation import org.jetbrains.kotlin.resolve.jvm.annotations.isJvmRecord import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations +import org.jetbrains.kotlin.utils.addToStdlib.safeAs open class JvmGeneratorExtensionsImpl( configuration: CompilerConfiguration, @@ -81,6 +84,10 @@ open class JvmGeneratorExtensionsImpl( companion object Instance : JvmSamConversion() } + override fun getContainerSource(descriptor: DeclarationDescriptor): DeserializedContainerSource? { + return descriptor.safeAs()?.containerSource + } + override fun computeFieldVisibility(descriptor: PropertyDescriptor): DescriptorVisibility? = if (descriptor.hasJvmFieldAnnotation() || descriptor is JavaCallableMemberDescriptor) descriptor.visibility @@ -98,7 +105,7 @@ open class JvmGeneratorExtensionsImpl( deserializedSource: DeserializedContainerSource, stubGenerator: DeclarationStubGenerator ): IrClass? { - if (!generateFacades || deserializedSource !is JvmPackagePartSource) return null + if (!generateFacades || deserializedSource !is FacadeClassSource) return null val facadeName = deserializedSource.facadeClassName ?: deserializedSource.className return JvmFileFacadeClass( if (deserializedSource.facadeClassName != null) IrDeclarationOrigin.JVM_MULTIFILE_CLASS else IrDeclarationOrigin.FILE_CLASS, diff --git a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt index a6e1d46b041..13ac0e81e79 100644 --- a/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt +++ b/compiler/ir/backend.jvm/entrypoint/src/org/jetbrains/kotlin/backend/jvm/JvmIrCodegenFactory.kt @@ -34,6 +34,8 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl +import org.jetbrains.kotlin.psi2ir.generators.fragments.EvaluatorFragmentInfo +import org.jetbrains.kotlin.psi2ir.generators.fragments.FragmentContext import org.jetbrains.kotlin.psi2ir.generators.generateTypicalIrProviderList import org.jetbrains.kotlin.psi2ir.preprocessing.SourceDeclarationsPreprocessor import org.jetbrains.kotlin.resolve.CleanableBindingContext @@ -44,6 +46,7 @@ open class JvmIrCodegenFactory( private val externalMangler: JvmDescriptorMangler? = null, private val externalSymbolTable: SymbolTable? = null, private val jvmGeneratorExtensions: JvmGeneratorExtensionsImpl = JvmGeneratorExtensionsImpl(configuration), + private val evaluatorFragmentInfoForPsi2Ir: EvaluatorFragmentInfo? = null ) : CodegenFactory { data class JvmIrBackendInput( val irModuleFragment: IrModuleFragment, @@ -65,7 +68,13 @@ open class JvmIrCodegenFactory( } val psi2ir = Psi2IrTranslator(input.languageVersionSettings, Psi2IrConfiguration(input.ignoreErrors)) val messageLogger = input.configuration[IrMessageLogger.IR_MESSAGE_LOGGER] ?: IrMessageLogger.None - val psi2irContext = psi2ir.createGeneratorContext(input.module, input.bindingContext, symbolTable, jvmGeneratorExtensions) + val psi2irContext = psi2ir.createGeneratorContext( + input.module, + input.bindingContext, + symbolTable, + jvmGeneratorExtensions, + fragmentContext = if (evaluatorFragmentInfoForPsi2Ir != null) FragmentContext() else null, + ) val pluginExtensions = IrGenerationExtension.getInstances(input.project) val stubGenerator = @@ -134,10 +143,22 @@ open class JvmIrCodegenFactory( } irLinker.deserializeIrModuleHeader(it, kotlinLibrary, _moduleName = it.name.asString()) } - val irProviders = listOf(irLinker) + + val irProviders = if (evaluatorFragmentInfoForPsi2Ir != null) { + listOf(stubGenerator, irLinker) + } else { + listOf(irLinker) + } val irModuleFragment = - psi2ir.generateModuleFragment(psi2irContext, input.files, irProviders, pluginExtensions, expectDescriptorToSymbol = null) + psi2ir.generateModuleFragment( + psi2irContext, + input.files, + irProviders, + pluginExtensions, + expectDescriptorToSymbol = null, + fragmentInfo = evaluatorFragmentInfoForPsi2Ir) + irLinker.postProcess() stubGenerator.unboundSymbolGeneration = true diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt index 9b42bc188de..a1b43604efb 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt @@ -31,6 +31,9 @@ import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl +import org.jetbrains.kotlin.psi2ir.generators.fragments.EvaluatorFragmentInfo +import org.jetbrains.kotlin.psi2ir.generators.fragments.FragmentContext +import org.jetbrains.kotlin.psi2ir.generators.fragments.FragmentModuleGenerator import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.utils.SmartList @@ -52,7 +55,8 @@ class Psi2IrTranslator( moduleDescriptor: ModuleDescriptor, bindingContext: BindingContext, symbolTable: SymbolTable, - extensions: GeneratorExtensions = GeneratorExtensions() + extensions: GeneratorExtensions = GeneratorExtensions(), + fragmentContext: FragmentContext? = null ): GeneratorContext { val typeTranslator = TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions) return GeneratorContext( @@ -64,6 +68,7 @@ class Psi2IrTranslator( extensions, typeTranslator, IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable), + fragmentContext ) } @@ -72,9 +77,13 @@ class Psi2IrTranslator( ktFiles: Collection, irProviders: List, linkerExtensions: Collection, - expectDescriptorToSymbol: MutableMap? = null + expectDescriptorToSymbol: MutableMap? = null, + fragmentInfo: EvaluatorFragmentInfo? = null ): IrModuleFragment { - val moduleGenerator = ModuleGenerator(context, expectDescriptorToSymbol) + val moduleGenerator = fragmentInfo?.let { + FragmentModuleGenerator(context, it) + } ?: ModuleGenerator(context, expectDescriptorToSymbol) + val irModule = moduleGenerator.generateModuleFragment(ktFiles) val deserializers = irProviders.filterIsInstance() diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index c1f94c47d38..fcf7c0d2e13 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt @@ -89,8 +89,16 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator resolvedCall: ResolvedCall<*>?, origin: IrStatementOrigin?, smartCastIrType: IrType? = null - ): IrExpression = - when (descriptor) { + ): IrExpression { + context.fragmentContext?.capturedDescriptorToFragmentParameterMap?.get(descriptor)?.let { + val getValue = IrGetValueImpl(startOffset, endOffset, it.descriptor.type.toIrType(), it, origin) + return if (smartCastIrType != null) { + IrTypeOperatorCallImpl(startOffset, endOffset, smartCastIrType, IrTypeOperator.IMPLICIT_CAST, smartCastIrType, getValue) + } else { + getValue + } + } + return when (descriptor) { is FakeCallableDescriptorForObject -> generateValueReference(startOffset, endOffset, descriptor.getReferencedDescriptor(), resolvedCall, origin, smartCastIrType) is TypeAliasDescriptor -> @@ -117,6 +125,7 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator else -> TODO("Unexpected callable descriptor: $descriptor ${descriptor::class.java.simpleName}") } + } private fun generateGetVariable( startOffset: Int, diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt index 341ee897c71..d3d3c232fb5 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt @@ -9,16 +9,18 @@ import org.jetbrains.kotlin.backend.common.SamTypeApproximator import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.NotFoundClasses import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.builders.IrGeneratorContext -import org.jetbrains.kotlin.ir.descriptors.IrBuiltInsOverDescriptors import org.jetbrains.kotlin.ir.expressions.IrDeclarationReference +import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.TypeTranslator import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration +import org.jetbrains.kotlin.psi2ir.generators.fragments.FragmentContext import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.storage.LockBasedStorageManager @@ -31,7 +33,8 @@ class GeneratorContext private constructor( val extensions: GeneratorExtensions, val typeTranslator: TypeTranslator, override val irBuiltIns: IrBuiltIns, - internal val callToSubstitutedDescriptorMap: MutableMap + internal val callToSubstitutedDescriptorMap: MutableMap, + internal val fragmentContext: FragmentContext?, ) : IrGeneratorContext { constructor( @@ -43,6 +46,7 @@ class GeneratorContext private constructor( extensions: GeneratorExtensions, typeTranslator: TypeTranslator, irBuiltIns: IrBuiltIns, + fragmentContext: FragmentContext? = null ) : this( configuration, moduleDescriptor, @@ -52,7 +56,8 @@ class GeneratorContext private constructor( extensions, typeTranslator, irBuiltIns, - mutableMapOf() + mutableMapOf(), + fragmentContext, ) val constantValueGenerator = typeTranslator.constantValueGenerator @@ -76,8 +81,8 @@ class GeneratorContext private constructor( extensions, TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions, ktFile = ktFile), irBuiltIns, - callToSubstitutedDescriptorMap + callToSubstitutedDescriptorMap, + fragmentContext, ) } - } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt index cb5d09b3b3b..e2c39f64687 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt @@ -34,12 +34,12 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile import org.jetbrains.kotlin.utils.addIfNotNull -class ModuleGenerator( +open class ModuleGenerator( override val context: GeneratorContext, private val expectDescriptorToSymbol: MutableMap? = null ) : Generator { - fun generateModuleFragment(ktFiles: Collection): IrModuleFragment = + open fun generateModuleFragment(ktFiles: Collection): IrModuleFragment = IrModuleFragmentImpl(context.moduleDescriptor, context.irBuiltIns).also { irModule -> ktFiles.toSet().mapTo(irModule.files) { ktFile -> val fileContext = context.createFileScopeContext(ktFile) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/EvaluatorFragmentInfo.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/EvaluatorFragmentInfo.kt new file mode 100644 index 00000000000..fd941e67415 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/EvaluatorFragmentInfo.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2021 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.psi2ir.generators.fragments + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor + +/** + * Information for compilation of code fragments for `evaluate expression` + * + * The expression evaluator works by wrapping a code fragment in a method. + * The free variables of the fragment are closed over by the parameters of + * that method, and finally the method is placed in a class. + * + * This data structure contains "synthesized" descriptors for that class, + * method and parameter lay-out. + */ +class EvaluatorFragmentInfo( + val classDescriptor: ClassDescriptor, + val methodDescriptor: FunctionDescriptor, + val parameters: List +) \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentCompilerSymbolTableDecorator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentCompilerSymbolTableDecorator.kt new file mode 100644 index 00000000000..387caa489f0 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentCompilerSymbolTableDecorator.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2010-2021 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.psi2ir.generators.fragments + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.ParameterDescriptor +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.descriptors.ValueDescriptor +import org.jetbrains.kotlin.ir.declarations.IrFactory +import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol +import org.jetbrains.kotlin.ir.symbols.IrValueSymbol +import org.jetbrains.kotlin.ir.util.IdSignatureComposer +import org.jetbrains.kotlin.ir.util.NameProvider +import org.jetbrains.kotlin.ir.util.SymbolTable +import org.jetbrains.kotlin.psi2ir.generators.fragments.EvaluatorFragmentInfo +import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver + +// Used from CodeFragmentCompiler for IDE Debugger Plug-In +@Suppress("unused") +class FragmentCompilerSymbolTableDecorator( + signatureComposer: IdSignatureComposer, + irFactory: IrFactory, + private val fragmentInfo: EvaluatorFragmentInfo, + nameProvider: NameProvider = NameProvider.DEFAULT, +) : SymbolTable(signatureComposer, irFactory, nameProvider) { + + override fun referenceValueParameter(descriptor: ParameterDescriptor): IrValueParameterSymbol { + if (descriptor !is ReceiverParameterDescriptor) return super.referenceValueParameter(descriptor) + + val finderPredicate = when (val receiverValue = descriptor.value) { + is ExtensionReceiver -> { targetDescriptor: DeclarationDescriptor -> + receiverValue == (targetDescriptor as? ReceiverParameterDescriptor)?.value + } + is ThisClassReceiver -> { targetDescriptor: DeclarationDescriptor -> + receiverValue.classDescriptor == targetDescriptor.original + } + else -> TODO("Unimplemented") + } + + val parameterPosition = + fragmentInfo.parameters.indexOfFirst(finderPredicate) + if (parameterPosition > -1) { + return super.referenceValueParameter(fragmentInfo.methodDescriptor.valueParameters[parameterPosition]) + } + return super.referenceValueParameter(descriptor) + } + + override fun referenceValue(value: ValueDescriptor): IrValueSymbol { + val parameterPosition = + fragmentInfo.parameters.indexOf(value) + if (parameterPosition > -1) { + return super.referenceValueParameter(fragmentInfo.methodDescriptor.valueParameters[parameterPosition]) + } + + return super.referenceValue(value) + } +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentContext.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentContext.kt new file mode 100644 index 00000000000..84257eb6b3d --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentContext.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2010-2021 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.psi2ir.generators.fragments + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol + +class FragmentContext( + val capturedDescriptorToFragmentParameterMap: MutableMap = mutableMapOf() +) \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentDeclarationGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentDeclarationGenerator.kt new file mode 100644 index 00000000000..38ef2764339 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentDeclarationGenerator.kt @@ -0,0 +1,129 @@ +/* + * Copyright 2010-2021 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.psi2ir.generators.fragments + +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorPublicSymbolImpl +import org.jetbrains.kotlin.ir.util.createIrClassFromDescriptor +import org.jetbrains.kotlin.ir.util.declareSimpleFunctionWithOverrides +import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.withScope +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtBlockCodeFragment +import org.jetbrains.kotlin.psi2ir.generators.Generator +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext +import org.jetbrains.kotlin.psi2ir.generators.createBodyGenerator +import org.jetbrains.kotlin.types.KotlinType + +open class FragmentDeclarationGenerator( + override val context: GeneratorContext, + private val fragmentInfo: EvaluatorFragmentInfo +) : Generator { + + fun generateClassForCodeFragment(ktFile: KtBlockCodeFragment): IrClass { + val classDescriptor = fragmentInfo.classDescriptor + val startOffset = UNDEFINED_OFFSET + val endOffset = UNDEFINED_OFFSET + + return context.symbolTable.declareClass(classDescriptor) { + context.irFactory.createIrClassFromDescriptor( + startOffset, endOffset, + IrDeclarationOrigin.DEFINED, + symbol = it, + classDescriptor, + context.symbolTable.nameProvider.nameForDeclaration(classDescriptor), + classDescriptor.visibility, + Modality.FINAL + ) + }.buildWithScope { irClass -> + irClass.thisReceiver = context.symbolTable.declareValueParameter( + startOffset, endOffset, + IrDeclarationOrigin.INSTANCE_RECEIVER, + classDescriptor.thisAsReceiverParameter, + classDescriptor.thisAsReceiverParameter.type.toIrType() + ) + + generatePrimaryConstructor(irClass) + + irClass.declarations.add( + generateFunctionForFragment(ktFile) + ) + } + } + + private fun generatePrimaryConstructor(irClass: IrClass) { + val constructor = context.irFactory.createConstructor( + startOffset = UNDEFINED_OFFSET, + endOffset = UNDEFINED_OFFSET, + origin = IrDeclarationOrigin.DEFINED, + symbol = IrConstructorPublicSymbolImpl(context.symbolTable.signaturer.composeSignature(irClass.descriptor)!!), + Name.special(""), + irClass.visibility, + irClass.defaultType, + isInline = false, + isExternal = false, + isPrimary = true, + isExpect = false + ) + constructor.parent = irClass + constructor.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) + irClass.addMember(constructor) + } + + private fun generateFunctionForFragment(ktFile: KtBlockCodeFragment): IrSimpleFunction { + return context.symbolTable.declareSimpleFunctionWithOverrides( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + IrDeclarationOrigin.DEFINED, + fragmentInfo.methodDescriptor + ).buildWithScope { irFunction -> + irFunction.returnType = fragmentInfo.methodDescriptor.returnType!!.toIrType() + generateFragmentValueParameterDeclarations(irFunction) + irFunction.body = createBodyGenerator(irFunction.symbol).generateExpressionBody(ktFile.getContentElement()) + } + } + + private fun generateFragmentValueParameterDeclarations(irFunction: IrSimpleFunction) { + val functionDescriptor = irFunction.descriptor + functionDescriptor.valueParameters.forEachIndexed { index, valueParameterDescriptor -> + irFunction.valueParameters += declareParameter(valueParameterDescriptor).apply { + context.fragmentContext!!.capturedDescriptorToFragmentParameterMap[fragmentInfo.parameters[index]] = this.symbol + } + } + } + + private fun declareParameter(descriptor: ValueParameterDescriptor): IrValueParameter { + // Parameter must be _assignable_: + // These parameters model the captured variables of the fragment. The captured + // _values_ are extracted from the call stack of the JVM being debugged, and supplied + // to the fragment evaluator via these parameters. Any modifications by the fragment + // are written directly to the parameter, and then extracted from the stack frame + // of the interpreter/JVM evaluating the fragment and written back into the call + // stack of the JVM being debugged. + return context.symbolTable.declareValueParameter( + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + IrDeclarationOrigin.DEFINED, + descriptor, + descriptor.type.toIrType(), + (descriptor as? ValueParameterDescriptor)?.varargElementType?.toIrType(), + null, + isAssignable = true + ) + } + + private fun KotlinType.toIrType() = context.typeTranslator.translateType(this) + + private inline fun T.buildWithScope(builder: (T) -> Unit): T = + also { irDeclaration: T -> + context.symbolTable.withScope(irDeclaration) { + builder(irDeclaration) + } + } + +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentModuleGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentModuleGenerator.kt new file mode 100644 index 00000000000..dd0ccd67287 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/fragments/FragmentModuleGenerator.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2021 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.psi2ir.generators.fragments + +import org.jetbrains.kotlin.ir.PsiIrFileEntry +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl +import org.jetbrains.kotlin.ir.util.patchDeclarationParents +import org.jetbrains.kotlin.psi.KtBlockCodeFragment +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext +import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator +import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile + +class FragmentModuleGenerator( + override val context: GeneratorContext, + private val fragmentInfo: EvaluatorFragmentInfo +) : ModuleGenerator(context, expectDescriptorToSymbol = null) { + + override fun generateModuleFragment( + ktFiles: Collection, + ): IrModuleFragment { + val ktBlockCodeFragment = ktFiles.singleOrNull() as? KtBlockCodeFragment + ?: TODO("Multiple fragments in one compilation not understood and implemented yet") + return IrModuleFragmentImpl(context.moduleDescriptor, context.irBuiltIns).also { irModule -> + val irDeclarationGenerator = FragmentDeclarationGenerator(context, fragmentInfo) + irModule.files.add( + createEmptyIrFile(ktBlockCodeFragment).apply { + declarations.add( + irDeclarationGenerator.generateClassForCodeFragment(ktBlockCodeFragment) + ) + patchDeclarationParents() + } + ) + } + } + + private fun createEmptyIrFile(ktFile: KtFile): IrFileImpl { + val fileEntry = PsiIrFileEntry(ktFile) + val packageFragmentDescriptor = context.moduleDescriptor.findPackageFragmentForFile(ktFile)!! + return IrFileImpl(fileEntry, packageFragmentDescriptor) + } +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/VariableLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/VariableLValue.kt index fa23aae2a0e..509a6183f67 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/VariableLValue.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/VariableLValue.kt @@ -48,7 +48,7 @@ class VariableLValue( IrSetValueImpl( startOffset, endOffset, context.irBuiltIns.unitType, - symbol.assertedCast { "Not a variable: ${symbol.descriptor}" }, + symbol, irExpression, origin ) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt index da91412813b..2e999c96029 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt @@ -92,7 +92,7 @@ abstract class DeclarationStubGenerator( fun generateOrGetFacadeClass(descriptor: DeclarationDescriptor): IrClass? { val directMember = descriptor.safeAs()?.correspondingProperty ?: descriptor val packageFragment = directMember.containingDeclaration as? PackageFragmentDescriptor ?: return null - val containerSource = directMember.safeAs()?.containerSource ?: return null + val containerSource = extensions.getContainerSource(directMember) ?: return null return facadeClassMap.getOrPut(containerSource) { extensions.generateFacadeClass(symbolTable.irFactory, containerSource, this)?.also { facade -> val packageStub = generateOrGetEmptyExternalPackageFragmentStub(packageFragment) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/StubGeneratorExtensions.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/StubGeneratorExtensions.kt index 58f4ba9ae18..d0bf61e9810 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/StubGeneratorExtensions.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/StubGeneratorExtensions.kt @@ -26,6 +26,15 @@ open class StubGeneratorExtensions { stubGenerator: DeclarationStubGenerator, ): IrClass? = null + + // Extension point for the JVM Debugger IDEA plug-in: it compiles fragments + // (conditions on breakpoints, "Evaluate expression...", watches, etc...) + // in the context of an open intellij project that is being debugged. These + // classes are supplied to the fragment evaluator as PSI, not class files, + // as the old backend assumes for external declarations. Hence, we need to + // intercept and supply "fake" deserialized sources. + open fun getContainerSource(descriptor: DeclarationDescriptor): DeserializedContainerSource? = null + open fun isPropertyWithPlatformField(descriptor: PropertyDescriptor): Boolean = false open fun isStaticFunction(descriptor: FunctionDescriptor): Boolean = false diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt index 27db7179a1c..e3983043389 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt @@ -60,7 +60,7 @@ interface ReferenceSymbolTable { fun leaveScope(owner: IrDeclaration) } -class SymbolTable( +open class SymbolTable( val signaturer: IdSignatureComposer, val irFactory: IrFactory, val nameProvider: NameProvider = NameProvider.DEFAULT @@ -1000,11 +1000,12 @@ class SymbolTable( type: IrType, varargElementType: IrType? = null, name: Name? = null, + isAssignable: Boolean = false, valueParameterFactory: (IrValueParameterSymbol) -> IrValueParameter = { irFactory.createValueParameter( startOffset, endOffset, origin, it, name ?: nameProvider.nameForDeclaration(descriptor), descriptor.indexOrMinusOne, type, varargElementType, descriptor.isCrossinline, descriptor.isNoinline, - isHidden = false, isAssignable = false + isHidden = false, isAssignable = isAssignable ) } ): IrValueParameter = @@ -1110,7 +1111,7 @@ class SymbolTable( leaveScope(owner.symbol) } - fun referenceValue(value: ValueDescriptor): IrValueSymbol = + open fun referenceValue(value: ValueDescriptor): IrValueSymbol = when (value) { is ParameterDescriptor -> valueParameterSymbolTable.referenced(value) { throw AssertionError("Undefined parameter referenced: $value") } diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/DescriptorByIdSignatureFinder.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/DescriptorByIdSignatureFinder.kt index a2d8a2da27a..91ef3394c66 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/DescriptorByIdSignatureFinder.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/DescriptorByIdSignatureFinder.kt @@ -22,7 +22,9 @@ class DescriptorByIdSignatureFinder( private val lookupMode: LookupMode ) { init { - assert(lookupMode != LookupMode.MODULE_ONLY || moduleDescriptor is ModuleDescriptorImpl) + assert(lookupMode != LookupMode.MODULE_ONLY || moduleDescriptor is ModuleDescriptorImpl) { + "Incorrect lookup mode $lookupMode for $moduleDescriptor" + } } /** diff --git a/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/FacadeClassSource.kt b/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/FacadeClassSource.kt new file mode 100644 index 00000000000..f3af1f3abd3 --- /dev/null +++ b/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/FacadeClassSource.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2010-2021 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.load.kotlin + +import org.jetbrains.kotlin.resolve.jvm.JvmClassName + +interface FacadeClassSource { + val className: JvmClassName + val facadeClassName: JvmClassName? +} \ No newline at end of file diff --git a/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartSource.kt b/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartSource.kt index 418779741af..6a59045c7b7 100644 --- a/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartSource.kt +++ b/core/deserialization.common.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartSource.kt @@ -20,15 +20,15 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource class JvmPackagePartSource( - val className: JvmClassName, - val facadeClassName: JvmClassName?, + override val className: JvmClassName, + override val facadeClassName: JvmClassName?, packageProto: ProtoBuf.Package, nameResolver: NameResolver, override val incompatibility: IncompatibleVersionErrorData? = null, override val isPreReleaseInvisible: Boolean = false, override val abiStability: DeserializedContainerAbiStability = DeserializedContainerAbiStability.STABLE, val knownJvmBinaryClass: KotlinJvmBinaryClass? = null -) : DeserializedContainerSource { +) : DeserializedContainerSource, FacadeClassSource { constructor( kotlinClass: KotlinJvmBinaryClass, packageProto: ProtoBuf.Package,