[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.
This commit is contained in:
Kristoffer Andersen
2021-08-13 14:51:26 +02:00
committed by Alexander Udalov
parent 783c3d1500
commit 247cbffbac
19 changed files with 421 additions and 25 deletions
@@ -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)
}
@@ -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.descriptors.getParentJavaStaticClassScope
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils
import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability 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.JvmPackagePartSource
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
import org.jetbrains.kotlin.name.FqName 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.hasJvmFieldAnnotation
import org.jetbrains.kotlin.resolve.jvm.annotations.isJvmRecord import org.jetbrains.kotlin.resolve.jvm.annotations.isJvmRecord
import org.jetbrains.kotlin.resolve.scopes.MemberScope 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.serialization.deserialization.descriptors.DeserializedContainerSource
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
open class JvmGeneratorExtensionsImpl( open class JvmGeneratorExtensionsImpl(
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
@@ -81,6 +84,10 @@ open class JvmGeneratorExtensionsImpl(
companion object Instance : JvmSamConversion() companion object Instance : JvmSamConversion()
} }
override fun getContainerSource(descriptor: DeclarationDescriptor): DeserializedContainerSource? {
return descriptor.safeAs<DescriptorWithContainerSource>()?.containerSource
}
override fun computeFieldVisibility(descriptor: PropertyDescriptor): DescriptorVisibility? = override fun computeFieldVisibility(descriptor: PropertyDescriptor): DescriptorVisibility? =
if (descriptor.hasJvmFieldAnnotation() || descriptor is JavaCallableMemberDescriptor) if (descriptor.hasJvmFieldAnnotation() || descriptor is JavaCallableMemberDescriptor)
descriptor.visibility descriptor.visibility
@@ -98,7 +105,7 @@ open class JvmGeneratorExtensionsImpl(
deserializedSource: DeserializedContainerSource, deserializedSource: DeserializedContainerSource,
stubGenerator: DeclarationStubGenerator stubGenerator: DeclarationStubGenerator
): IrClass? { ): IrClass? {
if (!generateFacades || deserializedSource !is JvmPackagePartSource) return null if (!generateFacades || deserializedSource !is FacadeClassSource) return null
val facadeName = deserializedSource.facadeClassName ?: deserializedSource.className val facadeName = deserializedSource.facadeClassName ?: deserializedSource.className
return JvmFileFacadeClass( return JvmFileFacadeClass(
if (deserializedSource.facadeClassName != null) IrDeclarationOrigin.JVM_MULTIFILE_CLASS else IrDeclarationOrigin.FILE_CLASS, if (deserializedSource.facadeClassName != null) IrDeclarationOrigin.JVM_MULTIFILE_CLASS else IrDeclarationOrigin.FILE_CLASS,
@@ -34,6 +34,8 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl 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.generators.generateTypicalIrProviderList
import org.jetbrains.kotlin.psi2ir.preprocessing.SourceDeclarationsPreprocessor import org.jetbrains.kotlin.psi2ir.preprocessing.SourceDeclarationsPreprocessor
import org.jetbrains.kotlin.resolve.CleanableBindingContext import org.jetbrains.kotlin.resolve.CleanableBindingContext
@@ -44,6 +46,7 @@ open class JvmIrCodegenFactory(
private val externalMangler: JvmDescriptorMangler? = null, private val externalMangler: JvmDescriptorMangler? = null,
private val externalSymbolTable: SymbolTable? = null, private val externalSymbolTable: SymbolTable? = null,
private val jvmGeneratorExtensions: JvmGeneratorExtensionsImpl = JvmGeneratorExtensionsImpl(configuration), private val jvmGeneratorExtensions: JvmGeneratorExtensionsImpl = JvmGeneratorExtensionsImpl(configuration),
private val evaluatorFragmentInfoForPsi2Ir: EvaluatorFragmentInfo? = null
) : CodegenFactory { ) : CodegenFactory {
data class JvmIrBackendInput( data class JvmIrBackendInput(
val irModuleFragment: IrModuleFragment, val irModuleFragment: IrModuleFragment,
@@ -65,7 +68,13 @@ open class JvmIrCodegenFactory(
} }
val psi2ir = Psi2IrTranslator(input.languageVersionSettings, Psi2IrConfiguration(input.ignoreErrors)) val psi2ir = Psi2IrTranslator(input.languageVersionSettings, Psi2IrConfiguration(input.ignoreErrors))
val messageLogger = input.configuration[IrMessageLogger.IR_MESSAGE_LOGGER] ?: IrMessageLogger.None 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 pluginExtensions = IrGenerationExtension.getInstances(input.project)
val stubGenerator = val stubGenerator =
@@ -134,10 +143,22 @@ open class JvmIrCodegenFactory(
} }
irLinker.deserializeIrModuleHeader(it, kotlinLibrary, _moduleName = it.name.asString()) 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 = val irModuleFragment =
psi2ir.generateModuleFragment(psi2irContext, input.files, irProviders, pluginExtensions, expectDescriptorToSymbol = null) psi2ir.generateModuleFragment(
psi2irContext,
input.files,
irProviders,
pluginExtensions,
expectDescriptorToSymbol = null,
fragmentInfo = evaluatorFragmentInfoForPsi2Ir)
irLinker.postProcess() irLinker.postProcess()
stubGenerator.unboundSymbolGeneration = true stubGenerator.unboundSymbolGeneration = true
@@ -31,6 +31,9 @@ import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator
import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl 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.resolve.BindingContext
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
@@ -52,7 +55,8 @@ class Psi2IrTranslator(
moduleDescriptor: ModuleDescriptor, moduleDescriptor: ModuleDescriptor,
bindingContext: BindingContext, bindingContext: BindingContext,
symbolTable: SymbolTable, symbolTable: SymbolTable,
extensions: GeneratorExtensions = GeneratorExtensions() extensions: GeneratorExtensions = GeneratorExtensions(),
fragmentContext: FragmentContext? = null
): GeneratorContext { ): GeneratorContext {
val typeTranslator = TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions) val typeTranslator = TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions)
return GeneratorContext( return GeneratorContext(
@@ -64,6 +68,7 @@ class Psi2IrTranslator(
extensions, extensions,
typeTranslator, typeTranslator,
IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable), IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable),
fragmentContext
) )
} }
@@ -72,9 +77,13 @@ class Psi2IrTranslator(
ktFiles: Collection<KtFile>, ktFiles: Collection<KtFile>,
irProviders: List<IrProvider>, irProviders: List<IrProvider>,
linkerExtensions: Collection<IrDeserializer.IrLinkerExtension>, linkerExtensions: Collection<IrDeserializer.IrLinkerExtension>,
expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>? = null expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>? = null,
fragmentInfo: EvaluatorFragmentInfo? = null
): IrModuleFragment { ): IrModuleFragment {
val moduleGenerator = ModuleGenerator(context, expectDescriptorToSymbol) val moduleGenerator = fragmentInfo?.let {
FragmentModuleGenerator(context, it)
} ?: ModuleGenerator(context, expectDescriptorToSymbol)
val irModule = moduleGenerator.generateModuleFragment(ktFiles) val irModule = moduleGenerator.generateModuleFragment(ktFiles)
val deserializers = irProviders.filterIsInstance<IrDeserializer>() val deserializers = irProviders.filterIsInstance<IrDeserializer>()
@@ -89,8 +89,16 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator
resolvedCall: ResolvedCall<*>?, resolvedCall: ResolvedCall<*>?,
origin: IrStatementOrigin?, origin: IrStatementOrigin?,
smartCastIrType: IrType? = null smartCastIrType: IrType? = null
): IrExpression = ): IrExpression {
when (descriptor) { 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 -> is FakeCallableDescriptorForObject ->
generateValueReference(startOffset, endOffset, descriptor.getReferencedDescriptor(), resolvedCall, origin, smartCastIrType) generateValueReference(startOffset, endOffset, descriptor.getReferencedDescriptor(), resolvedCall, origin, smartCastIrType)
is TypeAliasDescriptor -> is TypeAliasDescriptor ->
@@ -117,6 +125,7 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator
else -> else ->
TODO("Unexpected callable descriptor: $descriptor ${descriptor::class.java.simpleName}") TODO("Unexpected callable descriptor: $descriptor ${descriptor::class.java.simpleName}")
} }
}
private fun generateGetVariable( private fun generateGetVariable(
startOffset: Int, startOffset: Int,
@@ -9,16 +9,18 @@ import org.jetbrains.kotlin.backend.common.SamTypeApproximator
import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.NotFoundClasses import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.builders.IrGeneratorContext 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.expressions.IrDeclarationReference
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.TypeTranslator import org.jetbrains.kotlin.ir.util.TypeTranslator
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.generators.fragments.FragmentContext
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
@@ -31,7 +33,8 @@ class GeneratorContext private constructor(
val extensions: GeneratorExtensions, val extensions: GeneratorExtensions,
val typeTranslator: TypeTranslator, val typeTranslator: TypeTranslator,
override val irBuiltIns: IrBuiltIns, override val irBuiltIns: IrBuiltIns,
internal val callToSubstitutedDescriptorMap: MutableMap<IrDeclarationReference, CallableDescriptor> internal val callToSubstitutedDescriptorMap: MutableMap<IrDeclarationReference, CallableDescriptor>,
internal val fragmentContext: FragmentContext?,
) : IrGeneratorContext { ) : IrGeneratorContext {
constructor( constructor(
@@ -43,6 +46,7 @@ class GeneratorContext private constructor(
extensions: GeneratorExtensions, extensions: GeneratorExtensions,
typeTranslator: TypeTranslator, typeTranslator: TypeTranslator,
irBuiltIns: IrBuiltIns, irBuiltIns: IrBuiltIns,
fragmentContext: FragmentContext? = null
) : this( ) : this(
configuration, configuration,
moduleDescriptor, moduleDescriptor,
@@ -52,7 +56,8 @@ class GeneratorContext private constructor(
extensions, extensions,
typeTranslator, typeTranslator,
irBuiltIns, irBuiltIns,
mutableMapOf() mutableMapOf(),
fragmentContext,
) )
val constantValueGenerator = typeTranslator.constantValueGenerator val constantValueGenerator = typeTranslator.constantValueGenerator
@@ -76,8 +81,8 @@ class GeneratorContext private constructor(
extensions, extensions,
TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions, ktFile = ktFile), TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions, ktFile = ktFile),
irBuiltIns, irBuiltIns,
callToSubstitutedDescriptorMap callToSubstitutedDescriptorMap,
fragmentContext,
) )
} }
} }
@@ -34,12 +34,12 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
class ModuleGenerator( open class ModuleGenerator(
override val context: GeneratorContext, override val context: GeneratorContext,
private val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>? = null private val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>? = null
) : Generator { ) : Generator {
fun generateModuleFragment(ktFiles: Collection<KtFile>): IrModuleFragment = open fun generateModuleFragment(ktFiles: Collection<KtFile>): IrModuleFragment =
IrModuleFragmentImpl(context.moduleDescriptor, context.irBuiltIns).also { irModule -> IrModuleFragmentImpl(context.moduleDescriptor, context.irBuiltIns).also { irModule ->
ktFiles.toSet().mapTo(irModule.files) { ktFile -> ktFiles.toSet().mapTo(irModule.files) { ktFile ->
val fileContext = context.createFileScopeContext(ktFile) val fileContext = context.createFileScopeContext(ktFile)
@@ -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<DeclarationDescriptor>
)
@@ -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)
}
}
@@ -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<DeclarationDescriptor, IrValueParameterSymbol> = mutableMapOf()
)
@@ -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("<init>"),
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 : IrDeclaration> T.buildWithScope(builder: (T) -> Unit): T =
also { irDeclaration: T ->
context.symbolTable.withScope(irDeclaration) {
builder(irDeclaration)
}
}
}
@@ -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<KtFile>,
): 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)
}
}
@@ -48,7 +48,7 @@ class VariableLValue(
IrSetValueImpl( IrSetValueImpl(
startOffset, endOffset, startOffset, endOffset,
context.irBuiltIns.unitType, context.irBuiltIns.unitType,
symbol.assertedCast<IrVariableSymbol> { "Not a variable: ${symbol.descriptor}" }, symbol,
irExpression, origin irExpression, origin
) )
@@ -92,7 +92,7 @@ abstract class DeclarationStubGenerator(
fun generateOrGetFacadeClass(descriptor: DeclarationDescriptor): IrClass? { fun generateOrGetFacadeClass(descriptor: DeclarationDescriptor): IrClass? {
val directMember = descriptor.safeAs<PropertyAccessorDescriptor>()?.correspondingProperty ?: descriptor val directMember = descriptor.safeAs<PropertyAccessorDescriptor>()?.correspondingProperty ?: descriptor
val packageFragment = directMember.containingDeclaration as? PackageFragmentDescriptor ?: return null val packageFragment = directMember.containingDeclaration as? PackageFragmentDescriptor ?: return null
val containerSource = directMember.safeAs<DescriptorWithContainerSource>()?.containerSource ?: return null val containerSource = extensions.getContainerSource(directMember) ?: return null
return facadeClassMap.getOrPut(containerSource) { return facadeClassMap.getOrPut(containerSource) {
extensions.generateFacadeClass(symbolTable.irFactory, containerSource, this)?.also { facade -> extensions.generateFacadeClass(symbolTable.irFactory, containerSource, this)?.also { facade ->
val packageStub = generateOrGetEmptyExternalPackageFragmentStub(packageFragment) val packageStub = generateOrGetEmptyExternalPackageFragmentStub(packageFragment)
@@ -26,6 +26,15 @@ open class StubGeneratorExtensions {
stubGenerator: DeclarationStubGenerator, stubGenerator: DeclarationStubGenerator,
): IrClass? = null ): 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 isPropertyWithPlatformField(descriptor: PropertyDescriptor): Boolean = false
open fun isStaticFunction(descriptor: FunctionDescriptor): Boolean = false open fun isStaticFunction(descriptor: FunctionDescriptor): Boolean = false
@@ -60,7 +60,7 @@ interface ReferenceSymbolTable {
fun leaveScope(owner: IrDeclaration) fun leaveScope(owner: IrDeclaration)
} }
class SymbolTable( open class SymbolTable(
val signaturer: IdSignatureComposer, val signaturer: IdSignatureComposer,
val irFactory: IrFactory, val irFactory: IrFactory,
val nameProvider: NameProvider = NameProvider.DEFAULT val nameProvider: NameProvider = NameProvider.DEFAULT
@@ -1000,11 +1000,12 @@ class SymbolTable(
type: IrType, type: IrType,
varargElementType: IrType? = null, varargElementType: IrType? = null,
name: Name? = null, name: Name? = null,
isAssignable: Boolean = false,
valueParameterFactory: (IrValueParameterSymbol) -> IrValueParameter = { valueParameterFactory: (IrValueParameterSymbol) -> IrValueParameter = {
irFactory.createValueParameter( irFactory.createValueParameter(
startOffset, endOffset, origin, it, name ?: nameProvider.nameForDeclaration(descriptor), startOffset, endOffset, origin, it, name ?: nameProvider.nameForDeclaration(descriptor),
descriptor.indexOrMinusOne, type, varargElementType, descriptor.isCrossinline, descriptor.isNoinline, descriptor.indexOrMinusOne, type, varargElementType, descriptor.isCrossinline, descriptor.isNoinline,
isHidden = false, isAssignable = false isHidden = false, isAssignable = isAssignable
) )
} }
): IrValueParameter = ): IrValueParameter =
@@ -1110,7 +1111,7 @@ class SymbolTable(
leaveScope(owner.symbol) leaveScope(owner.symbol)
} }
fun referenceValue(value: ValueDescriptor): IrValueSymbol = open fun referenceValue(value: ValueDescriptor): IrValueSymbol =
when (value) { when (value) {
is ParameterDescriptor -> is ParameterDescriptor ->
valueParameterSymbolTable.referenced(value) { throw AssertionError("Undefined parameter referenced: $value") } valueParameterSymbolTable.referenced(value) { throw AssertionError("Undefined parameter referenced: $value") }
@@ -22,7 +22,9 @@ class DescriptorByIdSignatureFinder(
private val lookupMode: LookupMode private val lookupMode: LookupMode
) { ) {
init { init {
assert(lookupMode != LookupMode.MODULE_ONLY || moduleDescriptor is ModuleDescriptorImpl) assert(lookupMode != LookupMode.MODULE_ONLY || moduleDescriptor is ModuleDescriptorImpl) {
"Incorrect lookup mode $lookupMode for $moduleDescriptor"
}
} }
/** /**
@@ -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?
}
@@ -20,15 +20,15 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
class JvmPackagePartSource( class JvmPackagePartSource(
val className: JvmClassName, override val className: JvmClassName,
val facadeClassName: JvmClassName?, override val facadeClassName: JvmClassName?,
packageProto: ProtoBuf.Package, packageProto: ProtoBuf.Package,
nameResolver: NameResolver, nameResolver: NameResolver,
override val incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>? = null, override val incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>? = null,
override val isPreReleaseInvisible: Boolean = false, override val isPreReleaseInvisible: Boolean = false,
override val abiStability: DeserializedContainerAbiStability = DeserializedContainerAbiStability.STABLE, override val abiStability: DeserializedContainerAbiStability = DeserializedContainerAbiStability.STABLE,
val knownJvmBinaryClass: KotlinJvmBinaryClass? = null val knownJvmBinaryClass: KotlinJvmBinaryClass? = null
) : DeserializedContainerSource { ) : DeserializedContainerSource, FacadeClassSource {
constructor( constructor(
kotlinClass: KotlinJvmBinaryClass, kotlinClass: KotlinJvmBinaryClass,
packageProto: ProtoBuf.Package, packageProto: ProtoBuf.Package,