diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 6ac7712d364..1b02d5de747 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -22,9 +22,7 @@ import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.config.LanguageVersion.* -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.ScriptDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory @@ -423,6 +421,11 @@ class GenerationState private constructor( private fun shouldOnlyCollectSignatures(origin: JvmDeclarationOrigin) = classBuilderMode == ClassBuilderMode.LIGHT_CLASSES && origin.originKind in doNotGenerateInLightClassMode + val newFragmentCaptureParameters: MutableList> = mutableListOf() + fun recordNewFragmentCaptureParameter(string: String, type: KotlinType, descriptor: DeclarationDescriptor) { + newFragmentCaptureParameters.add(Triple(string, type, descriptor)) + } + companion object { val LANGUAGE_TO_METADATA_VERSION = EnumMap(LanguageVersion::class.java).apply { val oldMetadataVersion = JvmMetadataVersion(1, 1, 18) diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLocalVariableTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLocalVariableTestGenerated.java index d9c53e704ce..3607f26dba2 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLocalVariableTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLocalVariableTestGenerated.java @@ -193,6 +193,28 @@ public class FirLocalVariableTestGenerated extends AbstractFirLocalVariableTest runTest("compiler/testData/debug/localVariables/underscoreNames.kt"); } + @Nested + @TestMetadata("compiler/testData/debug/localVariables/constructors") + @TestDataPath("$PROJECT_ROOT") + public class Constructors { + @Test + public void testAllFilesPresentInConstructors() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/debug/localVariables/constructors"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("multipleConstructors.kt") + public void testMultipleConstructors() throws Exception { + runTest("compiler/testData/debug/localVariables/constructors/multipleConstructors.kt"); + } + + @Test + @TestMetadata("property.kt") + public void testProperty() throws Exception { + runTest("compiler/testData/debug/localVariables/constructors/property.kt"); + } + } + @Nested @TestMetadata("compiler/testData/debug/localVariables/receiverMangling") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirSteppingTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirSteppingTestGenerated.java index c2667d08728..3f595d3223b 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirSteppingTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirSteppingTestGenerated.java @@ -247,6 +247,18 @@ public class FirSteppingTestGenerated extends AbstractFirSteppingTest { runTest("compiler/testData/debug/stepping/inlineSimpleCall.kt"); } + @Test + @TestMetadata("kt15259.kt") + public void testKt15259() throws Exception { + runTest("compiler/testData/debug/stepping/kt15259.kt"); + } + + @Test + @TestMetadata("kt29179.kt") + public void testKt29179() throws Exception { + runTest("compiler/testData/debug/stepping/kt29179.kt"); + } + @Test @TestMetadata("kt42208.kt") public void testKt42208() throws Exception { @@ -337,6 +349,12 @@ public class FirSteppingTestGenerated extends AbstractFirSteppingTest { runTest("compiler/testData/debug/stepping/noParametersArgumentCallInExpression.kt"); } + @Test + @TestMetadata("nullcheck.kt") + public void testNullcheck() throws Exception { + runTest("compiler/testData/debug/stepping/nullcheck.kt"); + } + @Test @TestMetadata("primitiveNullChecks.kt") public void testPrimitiveNullChecks() throws Exception { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt index 3c08e53a63b..3a295319e4f 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt @@ -11,9 +11,9 @@ import org.jetbrains.kotlin.backend.common.descriptors.synthesizedString import org.jetbrains.kotlin.backend.common.ir.* import org.jetbrains.kotlin.backend.common.lower.inline.isInlineParameter import org.jetbrains.kotlin.backend.common.runOnFilePostfix -import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibility +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.* import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor import org.jetbrains.kotlin.ir.builders.declarations.buildFun @@ -71,7 +71,8 @@ class LocalDeclarationsLowering( val localNameSanitizer: (String) -> String = { it }, val visibilityPolicy: VisibilityPolicy = VisibilityPolicy.DEFAULT, val suggestUniqueNames: Boolean = true, // When `true` appends a `-#index` suffix to lifted declaration names - val forceFieldsForInlineCaptures: Boolean = false // See `LocalClassContext` + val forceFieldsForInlineCaptures: Boolean = false, // See `LocalClassContext` + private val postLocalDeclarationLoweringCallback: ((IntermediateDatastructures) -> Unit)? = null ) : BodyLoweringPass { @@ -109,7 +110,7 @@ class LocalDeclarationsLowering( ScopeWithCounter(this) } - private abstract class LocalContext { + abstract class LocalContext { val capturedTypeParameterToTypeParameter: MutableMap = mutableMapOf() // By the time typeRemapper is used, the map will be already filled @@ -121,7 +122,7 @@ class LocalDeclarationsLowering( abstract fun irGet(startOffset: Int, endOffset: Int, valueDeclaration: IrValueDeclaration): IrExpression? } - private abstract class LocalContextWithClosureAsParameters : LocalContext() { + abstract class LocalContextWithClosureAsParameters : LocalContext() { abstract val declaration: IrFunction abstract val transformedDeclaration: IrFunction @@ -135,7 +136,7 @@ class LocalDeclarationsLowering( } } - private class LocalFunctionContext( + class LocalFunctionContext( override val declaration: IrSimpleFunction, val index: Int, val ownerForLoweredDeclaration: IrDeclarationContainer @@ -246,6 +247,10 @@ class LocalDeclarationsLowering( rewriteDeclarations() insertLoweredDeclarationForLocalFunctions() + + postLocalDeclarationLoweringCallback?.invoke( + IntermediateDatastructures(localFunctions, newParameterToOld, newParameterToCaptured) + ) } private fun insertLoweredDeclarationForLocalFunctions() { @@ -967,6 +972,12 @@ class LocalDeclarationsLowering( }, Data(null, false)) } } + + data class IntermediateDatastructures( + val localFunctions: Map, + val newParameterToOld: Map, + val newParameterToCaptured: Map + ) } // Local inner classes capture anything through outer 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 b56099496dd..6d145ec240e 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 @@ -56,7 +56,6 @@ open class JvmIrCodegenFactory( private val externalMangler: JvmDescriptorMangler? = null, private val externalSymbolTable: SymbolTable? = null, private val jvmGeneratorExtensions: JvmGeneratorExtensionsImpl = JvmGeneratorExtensionsImpl(configuration), - private val prefixPhases: CompilerPhase? = null, private val evaluatorFragmentInfoForPsi2Ir: EvaluatorFragmentInfo? = null, private val shouldStubAndNotLinkUnboundSymbols: Boolean = false, ) : CodegenFactory { @@ -258,11 +257,14 @@ open class JvmIrCodegenFactory( ) JvmIrSerializerImpl(state.configuration) else null - val phases = prefixPhases?.then(jvmLoweringPhases) ?: jvmLoweringPhases + val phases = if (evaluatorFragmentInfoForPsi2Ir != null) jvmFragmentLoweringPhases else jvmLoweringPhases val phaseConfig = customPhaseConfig ?: PhaseConfig(phases) val context = JvmBackendContext( state, irModuleFragment.irBuiltins, irModuleFragment, symbolTable, phaseConfig, extensions, backendExtension, irSerializer, ) + if (evaluatorFragmentInfoForPsi2Ir != null) { + context.localDeclarationsLoweringData = mutableMapOf() + } val intrinsics by lazy { IrIntrinsicMethods(irModuleFragment.irBuiltins, context.ir.symbols) } context.getIntrinsic = { symbol: IrFunctionSymbol -> intrinsics.getIntrinsic(symbol) } /* JvmBackendContext creates new unbound symbols, have to resolve them. */ diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index b6e437a3ede..dc8455422d1 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -90,7 +90,7 @@ private val arrayConstructorPhase = makeIrFilePhase( description = "Transform `Array(size) { index -> value }` into a loop" ) -internal val expectDeclarationsRemovingPhase = makeIrModulePhase( +internal val expectDeclarationsRemovingPhase = makeIrModulePhase( ::ExpectDeclarationRemover, name = "ExpectDeclarationsRemoving", description = "Remove expect declaration from module fragment" @@ -139,7 +139,15 @@ internal val localDeclarationsPhase = makeIrFilePhase( private fun scopedVisibility(inInlineFunctionScope: Boolean): DescriptorVisibility = if (inInlineFunctionScope) DescriptorVisibilities.PUBLIC else JavaDescriptorVisibilities.PACKAGE_VISIBILITY }, - forceFieldsForInlineCaptures = true + forceFieldsForInlineCaptures = true, + postLocalDeclarationLoweringCallback = context.localDeclarationsLoweringData?.let { + { data -> + data.localFunctions.forEach { (localFunction, localContext) -> + it[localFunction] = + JvmBackendContext.LocalFunctionData(localContext, data.newParameterToOld, data.newParameterToCaptured) + } + } + } ) }, name = "JvmLocalDeclarations", @@ -383,23 +391,61 @@ private val jvmFilePhases = listOf( // makePatchParentsPhase() ) -val jvmLoweringPhases = NamedCompilerPhase( - name = "IrLowering", - description = "IR lowering", - nlevels = 1, - actions = setOf(defaultDumper, validationAction), - lower = validateIrBeforeLowering then - processOptionalAnnotationsPhase then - expectDeclarationsRemovingPhase then - serializeIrPhase then - scriptsToClassesPhase then - fileClassPhase then - jvmStaticInObjectPhase then - repeatedAnnotationPhase then - performByIrFile(lower = jvmFilePhases) then - generateMultifileFacadesPhase then - resolveInlineCallsPhase then - // should be last transformation - prepareForBytecodeInlining then - validateIrAfterLowering -) +val jvmLoweringPhases = buildJvmLoweringPhases("IrLowering", listOf("PerformByIrFile" to jvmFilePhases)) + +private fun buildJvmLoweringPhases( + name: String, + phases: List>>> +): NamedCompilerPhase { + return NamedCompilerPhase( + name = name, + description = "IR lowering", + nlevels = 1, + actions = setOf(defaultDumper, validationAction), + lower = + fragmentSharedVariablesLowering then + validateIrBeforeLowering then + processOptionalAnnotationsPhase then + expectDeclarationsRemovingPhase then + serializeIrPhase then + scriptsToClassesPhase then + fileClassPhase then + jvmStaticInObjectPhase then + repeatedAnnotationPhase then + buildLoweringsPhase(phases) then + generateMultifileFacadesPhase then + resolveInlineCallsPhase then + // should be last transformation + prepareForBytecodeInlining then + validateIrAfterLowering + ) +} + +// Build a compiler phase from a list of lowering sequences: each subsequence is run +// in parallel per file, and each parallel composition is run in sequence. +private fun buildLoweringsPhase( + perModuleLowerings: List>>>, +): CompilerPhase = + perModuleLowerings.map { (name, lowerings) -> performByIrFile(name, lower = lowerings) } + .reduce< + CompilerPhase, + CompilerPhase + > { result, phase -> result then phase } + + +val jvmFragmentLoweringPhases = run { + val localDeclarationsIndex = jvmFilePhases.indexOf(localDeclarationsPhase) + val loweringsUpToLocalDeclarations = jvmFilePhases.subList(0, localDeclarationsIndex + 1) + val remainingLowerings = jvmFilePhases.subList(localDeclarationsIndex + 1, jvmFilePhases.size) + buildJvmLoweringPhases( + "IrFragmentLowering", + listOf( + "PrefixOfIRPhases" to loweringsUpToLocalDeclarations, + "FragmentLowerings" to listOf( + fragmentLocalFunctionPatchLowering, + reflectiveAccessLowering, + ), + "SuffixOfIRPhases" to remainingLowerings + ) + ) +} diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FragmentLocalFunctionPatchLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FragmentLocalFunctionPatchLowering.kt new file mode 100644 index 00000000000..1013702e578 --- /dev/null +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FragmentLocalFunctionPatchLowering.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2010-2022 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.IrElementTransformerVoidWithContext +import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering +import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder +import org.jetbrains.kotlin.backend.jvm.localDeclarationsPhase +import org.jetbrains.kotlin.backend.jvm.lower.FragmentSharedVariablesLowering.Companion.GENERATED_FUNCTION_NAME +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter +import org.jetbrains.kotlin.ir.builders.irCall +import org.jetbrains.kotlin.ir.builders.irGet +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor +import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + + +// Used from CodeFragmentCompiler for IDE Debugger Plug-In +@Suppress("unused") +val fragmentLocalFunctionPatchLowering = makeIrFilePhase( + ::FragmentLocalFunctionPatchLowering, + name = "FragmentLocalFunctionPatching", + description = "Rewrite calls to local functions to the appropriate, lifted function created by local declarations lowering.", + prerequisite = setOf(localDeclarationsPhase) +) + +// This lowering rewrites local function calls in code fragments to the +// corresponding lifted declaration. In the process, the lowering determines +// whether the captures of the local function are a subset of the captures of +// the fragment, and if not, introduces additional captures to the fragment +// wrapper. The captures are then supplied to the fragment wrapper as +// parameters supplied at evaluation time. +internal class FragmentLocalFunctionPatchLowering( + val context: JvmBackendContext +) : IrElementTransformerVoidWithContext(), FileLoweringPass { + + lateinit var localDeclarationsData: Map + + override fun lower(irFile: IrFile) { + context.localDeclarationsLoweringData?.let { + localDeclarationsData = it + } ?: return + irFile.transformChildrenVoid(this) + } + + override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement { + if (declaration.name.asString() != GENERATED_FUNCTION_NAME) return declaration + + declaration.body!!.transformChildrenVoid(object : IrElementTransformerVoidWithContext() { + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid(this) + val localsData = localDeclarationsData[expression.symbol.owner] ?: return super.visitCall(expression) + val remappedTarget: LocalDeclarationsLowering.LocalFunctionContext = localsData.localContext + + val irBuilder = context.createJvmIrBuilder(declaration.symbol) + return irBuilder.irCall(remappedTarget.transformedDeclaration).apply { + this.copyTypeArgumentsFrom(expression) + extensionReceiver = expression.extensionReceiver + dispatchReceiver = expression.dispatchReceiver + + remappedTarget.transformedDeclaration.valueParameters.map { newValueParameterDeclaration -> + val oldParameter = localsData.newParameterToOld[newValueParameterDeclaration] + + val getValue = if (oldParameter != null) { + // the parameter is an actual parameter to the local + // function, not a parameter corresponding to a + // capture introduced by a lowering: fetch + // the corresponding argument from the existing + // call and place at the appropriate slot in the + // call to the lowered function + expression.getValueArgument(oldParameter.index)!! + } else { + // The parameter is introduced by the lowering to + // private static function, so corresponds to a _capture_ by the local function + val capturedValueSymbol = + localsData.newParameterToCaptured[newValueParameterDeclaration] + ?: error("Non-mapped parameter $newValueParameterDeclaration") + + // We introduce a new parameter to the _fragment function_ surrounding the call to the + // lowered local function, and supply _that_ parameter to the corresponding _argument_ slot + // in the call to the lowered function. + val newParameter = declaration.addValueParameter { + type = capturedValueSymbol.owner.type + name = capturedValueSymbol.owner.name + } + + context.state.recordNewFragmentCaptureParameter( + newParameter.name.asString(), + capturedValueSymbol.owner.type.toIrBasedKotlinType(), + capturedValueSymbol.owner.toIrBasedDescriptor() + ) + + irBuilder.irGet(newParameter) + } + + putValueArgument(newValueParameterDeclaration.index, getValue) + } + } + } + + }) + + return declaration + } +} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReflectiveAccess.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReflectiveAccess.kt index a37f5090130..44b0cb3ed56 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReflectiveAccess.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReflectiveAccess.kt @@ -5,32 +5,35 @@ package org.jetbrains.kotlin.backend.jvm.lower +import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext +import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.backend.jvm.ir.IrInlineScopeResolver -import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder -import org.jetbrains.kotlin.backend.jvm.ir.findInlineCallSites -import org.jetbrains.kotlin.backend.jvm.ir.isJvmInterface +import org.jetbrains.kotlin.backend.jvm.ir.* import org.jetbrains.kotlin.backend.jvm.lower.SyntheticAccessorLowering.Companion.isAccessible +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +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.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.defaultType -import org.jetbrains.kotlin.ir.types.starProjectedType +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject +import org.jetbrains.kotlin.utils.addToStdlib.safeAs // Used from CodeFragmentCompiler for IDE Debugger Plug-In @Suppress("unused") -val reflectiveAccessLowering = makeIrModulePhase( +val reflectiveAccessLowering = makeIrFilePhase( ::ReflectiveAccessLowering, name = "ReflectiveCalls", description = "Avoid the need for accessors by replacing direct access to inaccessible members with accesses via reflection", @@ -80,14 +83,18 @@ internal class ReflectiveAccessLowering( // Fragments are transformed in a post-order traversal: children first, // then parent. This obscures, in particular, dispatch receivers, that go // from `IrGetObjectValue` calls to blocks implementing the corresponding - // reflective access . We record these _before_ transformation, in order to + // reflective access. We record these _before_ transformation, in order to // later predict the compilation strategy for fields. See the uses of // `fieldLocationAndReceiver`. val callsOnCompanionObjects: MutableMap = mutableMapOf() + @OptIn(ObsoleteDescriptorBasedAPI::class) private fun recordCompanionObjectAsDispatchReceiver(expression: IrCall) { - if ((expression.dispatchReceiver as? IrGetObjectValue)?.symbol?.owner?.isCompanion == true) { - callsOnCompanionObjects[expression] = (expression.dispatchReceiver!! as IrGetObjectValue).symbol + val dispatchReceiver = expression.dispatchReceiver as? IrGetField ?: return + val dispatchReceiverType = dispatchReceiver.symbol.owner.type as? IrSimpleType ?: return + val klass = dispatchReceiverType.classOrNull + if (klass != null && klass.owner.isCompanion) { + callsOnCompanionObjects[expression] = klass } } @@ -207,6 +214,20 @@ internal class ReflectiveAccessLowering( putValueArgument(0, receiver) } + private fun createBuilder(startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET) = + context.createJvmIrBuilder(currentScope!!, startOffset, endOffset) + + private fun IrBuilderWithScope.irVararg( + elementType: IrType, + values: List + ): IrExpression { + return IrArrayBuilder(createBuilder(), context.irBuiltIns.arrayClass.typeWith(elementType)).apply { + for (value in values) { + +value + } + }.build() + } + private fun IrBuilderWithScope.getDeclaredMethod( declaringClass: IrExpression, methodName: String, @@ -293,13 +314,13 @@ internal class ReflectiveAccessLowering( private fun generateReflectiveMethodInvocation(call: IrCall): IrExpression = generateReflectiveMethodInvocation( - call.superQualifierSymbol?.defaultType ?: call.dispatchReceiver!!.type, + call.superQualifierSymbol?.defaultType ?: call.dispatchReceiver?.type ?: call.symbol.owner.parentAsClass.defaultType, call.symbol.owner.name.asString(), mutableListOf().apply { call.symbol.owner.extensionReceiverParameter?.let { add(it.type) } addAll(call.valueParameterTypes()) }, - call.dispatchReceiver!!, + call.dispatchReceiver, mutableListOf().apply { call.extensionReceiver?.let { add(it) } addAll(call.getValueArguments()) 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 53e932d9bcb..99e68df8a81 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 @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.DefaultMapping import org.jetbrains.kotlin.backend.common.Mapping import org.jetbrains.kotlin.backend.common.ir.Ir +import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.jvm.caches.BridgeLoweringCache import org.jetbrains.kotlin.backend.jvm.caches.CollectionStubComputer @@ -47,6 +48,18 @@ class JvmBackendContext( val backendExtension: JvmBackendExtension, val irSerializer: JvmIrSerializer?, ) : CommonBackendContext { + + data class LocalFunctionData( + val localContext: LocalDeclarationsLowering.LocalFunctionContext, + val newParameterToOld: Map, + val newParameterToCaptured: Map + ) + + // If not-null, this is populated by LocalDeclarationsLowering with the intermediate data + // allowing mapping from local function captures to parameters and accurate transformation + // of calls to local functions from code fragments (i.e. the expression evaluator). + var localDeclarationsLoweringData: MutableMap? = null + // If the JVM fqname of a class differs from what is implied by its parent, e.g. if it's a file class // annotated with @JvmPackageName, the correct name is recorded here. val classNameOverride: MutableMap 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 f93b373418c..842fcd89e5c 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 @@ -36,7 +36,7 @@ class GeneratorContext private constructor( val typeTranslator: TypeTranslator, override val irBuiltIns: IrBuiltIns, internal val callToSubstitutedDescriptorMap: MutableMap, - internal val fragmentContext: FragmentContext?, + internal var fragmentContext: FragmentContext?, ) : IrGeneratorContext { constructor( 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 index 02341613221..41850af6889 100644 --- 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 @@ -5,7 +5,6 @@ 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 @@ -23,11 +22,13 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver class FragmentCompilerSymbolTableDecorator( signatureComposer: IdSignatureComposer, irFactory: IrFactory, - private val fragmentInfo: EvaluatorFragmentInfo, + var fragmentInfo: EvaluatorFragmentInfo?, nameProvider: NameProvider = NameProvider.DEFAULT, ) : SymbolTable(signatureComposer, irFactory, nameProvider) { override fun referenceValueParameter(descriptor: ParameterDescriptor): IrValueParameterSymbol { + val fi = fragmentInfo ?: return super.referenceValueParameter(descriptor) + if (descriptor !is ReceiverParameterDescriptor) return super.referenceValueParameter(descriptor) val finderPredicate = when (val receiverValue = descriptor.value) { @@ -41,18 +42,20 @@ class FragmentCompilerSymbolTableDecorator( } val parameterPosition = - fragmentInfo.parameters.indexOfFirst(finderPredicate) + fi.parameters.indexOfFirst(finderPredicate) if (parameterPosition > -1) { - return super.referenceValueParameter(fragmentInfo.methodDescriptor.valueParameters[parameterPosition]) + return super.referenceValueParameter(fi.methodDescriptor.valueParameters[parameterPosition]) } return super.referenceValueParameter(descriptor) } override fun referenceValue(value: ValueDescriptor): IrValueSymbol { + val fi = fragmentInfo ?: return super.referenceValue(value) + val parameterPosition = - fragmentInfo.parameters.indexOfFirst { it.descriptor == value } + fi.parameters.indexOfFirst { it.descriptor == value } if (parameterPosition > -1) { - return super.referenceValueParameter(fragmentInfo.methodDescriptor.valueParameters[parameterPosition]) + return super.referenceValueParameter(fi.methodDescriptor.valueParameters[parameterPosition]) } return super.referenceValue(value) 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 index 08c6a80b32e..d759ea78f6c 100644 --- 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 @@ -38,14 +38,28 @@ class FragmentModuleGenerator( patchDeclarationParents() } } else { - val fileContext = context.createFileScopeContext(ktFile) - generateSingleFile(DeclarationGenerator(fileContext), ktFile, irModule) + generateInContextWithoutFragmentInfo(ktFile) { + generateSingleFile(DeclarationGenerator(it), ktFile, irModule) + } } ) } } } + private fun generateInContextWithoutFragmentInfo(ktFile: KtFile, block: (GeneratorContext) -> T): T { + val symbolTableDecorator = context.symbolTable as FragmentCompilerSymbolTableDecorator + val fragmentInfo = symbolTableDecorator.fragmentInfo + symbolTableDecorator.fragmentInfo = null + + val fileContext = context.createFileScopeContext(ktFile) + fileContext.fragmentContext = null + + return block(fileContext).also { + symbolTableDecorator.fragmentInfo = fragmentInfo + } + } + private fun createEmptyIrFile(ktFile: KtFile): IrFileImpl { val fileEntry = PsiIrFileEntry(ktFile) val packageFragmentDescriptor = context.moduleDescriptor.findPackageFragmentForFile(ktFile)!! diff --git a/compiler/testData/debug/localVariables/constructors/multipleConstructors.kt b/compiler/testData/debug/localVariables/constructors/multipleConstructors.kt new file mode 100644 index 00000000000..b98a5313b0d --- /dev/null +++ b/compiler/testData/debug/localVariables/constructors/multipleConstructors.kt @@ -0,0 +1,38 @@ + + +// FILE: test.kt +open class Base(i: Int) + +class Derived(): Base(1) { + constructor(p: Int): this() { + val a = 2 + } + + constructor(p1: Int, p2: Int): this() +} + +fun box() { + Derived(3) + Derived(4, 5) +} + +// EXPECTATIONS +// test.kt:15 box: +// test.kt:7 : p:int=3:int +// test.kt:6 : +// test.kt:4 : i:int=1:int +// test.kt:6 : +// test.kt:8 : p:int=3:int +// EXPECTATIONS JVM_IR +// test.kt:9 : p:int=3:int, a:int=2:int +// EXPECTATIONS +// test.kt:15 box: + +// test.kt:16 box: +// test.kt:11 : p1:int=4:int, p2:int=5:int +// test.kt:6 : +// test.kt:4 : i:int=1:int +// test.kt:6 : +// test.kt:11 : p1:int=4:int, p2:int=5:int +// test.kt:16 box: +// test.kt:17 box: \ No newline at end of file diff --git a/compiler/testData/debug/localVariables/constructors/property.kt b/compiler/testData/debug/localVariables/constructors/property.kt new file mode 100644 index 00000000000..7351789b045 --- /dev/null +++ b/compiler/testData/debug/localVariables/constructors/property.kt @@ -0,0 +1,14 @@ + + +// FILE: test.kt +class F(val a: String) + +fun box() { + F("foo") +} + +// EXPECTATIONS +// test.kt:7 box: +// test.kt:4 : a:java.lang.String="foo":java.lang.String +// test.kt:7 box: +// test.kt:8 box: \ No newline at end of file diff --git a/compiler/testData/debug/stepping/kt15259.kt b/compiler/testData/debug/stepping/kt15259.kt new file mode 100644 index 00000000000..f3e8c80902c --- /dev/null +++ b/compiler/testData/debug/stepping/kt15259.kt @@ -0,0 +1,24 @@ +// IGNORE_BACKEND: JVM_IR +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt +interface ObjectFace + +private fun makeFace() = object : ObjectFace { + + init { 5 } +} + +fun box() { + makeFace() +} + +// IR backend has additional steps on the way _out_ of the init block. + +// EXPECTATIONS +// test.kt:12 box +// test.kt:6 makeFace +// test.kt:6 +// test.kt:8 +// test.kt:9 makeFace +// test.kt:12 box +// test.kt:13 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/kt29179.kt b/compiler/testData/debug/stepping/kt29179.kt new file mode 100644 index 00000000000..c64397ad816 --- /dev/null +++ b/compiler/testData/debug/stepping/kt29179.kt @@ -0,0 +1,26 @@ +// IGNORE_BACKEND: JVM_IR +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt +class A { + val a = 1 + fun bar() = 2 + fun foo() { + 3 + //Breakpoint! from the Evaluate Expression test suite. + .toString() + } +} + +fun box() { + A().foo() +} + +// EXPECTATIONS +// test.kt:15 box +// test.kt:4 +// test.kt:5 +// test.kt:15 box +// test.kt:8 foo +// test.kt:10 foo +// test.kt:11 foo +// test.kt:16 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/nullcheck.kt b/compiler/testData/debug/stepping/nullcheck.kt new file mode 100644 index 00000000000..80dc20e47e2 --- /dev/null +++ b/compiler/testData/debug/stepping/nullcheck.kt @@ -0,0 +1,68 @@ + + + +// FILE: test.kt +fun box() { + test("OK") + test(null) + testExpressionBody("OK") + testExpressionBody(null) +} + +fun test(nullable: String?): Boolean { + return nullable != null && + // Some comment + nullable.length == 2 +} + +fun testExpressionBody(nullable: String?) = + nullable != null && + // Some comment + nullable.length == 2 + +// EXPECTATIONS +// EXPECTATIONS JVM +// test.kt:6 box +// test.kt:15 test +// test.kt:13 test +// test.kt:6 box + +// test.kt:7 box +// test.kt:15 test +// test.kt:13 test +// test.kt:7 box + +// test.kt:8 box +// test.kt:21 testExpressionBody +// test.kt:8 box + +// test.kt:9 box +// test.kt:21 testExpressionBody +// test.kt:9 box + +// test.kt:10 box + +// EXPECTATIONS JVM_IR +// test.kt:6 box +// test.kt:13 test +// test.kt:15 test +// test.kt:13 test +// test.kt:6 box + +// test.kt:7 box +// test.kt:13 test +// test.kt:15 test +// test.kt:13 test +// test.kt:7 box + +// test.kt:8 box +// test.kt:19 testExpressionBody +// test.kt:21 testExpressionBody +// test.kt:8 box + +// test.kt:9 box +// test.kt:19 testExpressionBody +// test.kt:21 testExpressionBody +// test.kt:9 box + +// test.kt:10 box \ No newline at end of file diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrLocalVariableTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrLocalVariableTestGenerated.java index 524f6c5899d..349a8f9714c 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrLocalVariableTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrLocalVariableTestGenerated.java @@ -193,6 +193,28 @@ public class IrLocalVariableTestGenerated extends AbstractIrLocalVariableTest { runTest("compiler/testData/debug/localVariables/underscoreNames.kt"); } + @Nested + @TestMetadata("compiler/testData/debug/localVariables/constructors") + @TestDataPath("$PROJECT_ROOT") + public class Constructors { + @Test + public void testAllFilesPresentInConstructors() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/debug/localVariables/constructors"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("multipleConstructors.kt") + public void testMultipleConstructors() throws Exception { + runTest("compiler/testData/debug/localVariables/constructors/multipleConstructors.kt"); + } + + @Test + @TestMetadata("property.kt") + public void testProperty() throws Exception { + runTest("compiler/testData/debug/localVariables/constructors/property.kt"); + } + } + @Nested @TestMetadata("compiler/testData/debug/localVariables/receiverMangling") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrSteppingTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrSteppingTestGenerated.java index 2bbb3aea0dd..85d759c7fcc 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrSteppingTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrSteppingTestGenerated.java @@ -247,6 +247,18 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest { runTest("compiler/testData/debug/stepping/inlineSimpleCall.kt"); } + @Test + @TestMetadata("kt15259.kt") + public void testKt15259() throws Exception { + runTest("compiler/testData/debug/stepping/kt15259.kt"); + } + + @Test + @TestMetadata("kt29179.kt") + public void testKt29179() throws Exception { + runTest("compiler/testData/debug/stepping/kt29179.kt"); + } + @Test @TestMetadata("kt42208.kt") public void testKt42208() throws Exception { @@ -337,6 +349,12 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest { runTest("compiler/testData/debug/stepping/noParametersArgumentCallInExpression.kt"); } + @Test + @TestMetadata("nullcheck.kt") + public void testNullcheck() throws Exception { + runTest("compiler/testData/debug/stepping/nullcheck.kt"); + } + @Test @TestMetadata("primitiveNullChecks.kt") public void testPrimitiveNullChecks() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/LocalVariableTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/LocalVariableTestGenerated.java index 332011a5bea..870b7e820a9 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/LocalVariableTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/LocalVariableTestGenerated.java @@ -193,6 +193,28 @@ public class LocalVariableTestGenerated extends AbstractLocalVariableTest { runTest("compiler/testData/debug/localVariables/underscoreNames.kt"); } + @Nested + @TestMetadata("compiler/testData/debug/localVariables/constructors") + @TestDataPath("$PROJECT_ROOT") + public class Constructors { + @Test + public void testAllFilesPresentInConstructors() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/debug/localVariables/constructors"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @Test + @TestMetadata("multipleConstructors.kt") + public void testMultipleConstructors() throws Exception { + runTest("compiler/testData/debug/localVariables/constructors/multipleConstructors.kt"); + } + + @Test + @TestMetadata("property.kt") + public void testProperty() throws Exception { + runTest("compiler/testData/debug/localVariables/constructors/property.kt"); + } + } + @Nested @TestMetadata("compiler/testData/debug/localVariables/receiverMangling") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/SteppingTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/SteppingTestGenerated.java index adb08ec8d15..f040fca486d 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/SteppingTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/SteppingTestGenerated.java @@ -247,6 +247,18 @@ public class SteppingTestGenerated extends AbstractSteppingTest { runTest("compiler/testData/debug/stepping/inlineSimpleCall.kt"); } + @Test + @TestMetadata("kt15259.kt") + public void testKt15259() throws Exception { + runTest("compiler/testData/debug/stepping/kt15259.kt"); + } + + @Test + @TestMetadata("kt29179.kt") + public void testKt29179() throws Exception { + runTest("compiler/testData/debug/stepping/kt29179.kt"); + } + @Test @TestMetadata("kt42208.kt") public void testKt42208() throws Exception { @@ -337,6 +349,12 @@ public class SteppingTestGenerated extends AbstractSteppingTest { runTest("compiler/testData/debug/stepping/noParametersArgumentCallInExpression.kt"); } + @Test + @TestMetadata("nullcheck.kt") + public void testNullcheck() throws Exception { + runTest("compiler/testData/debug/stepping/nullcheck.kt"); + } + @Test @TestMetadata("primitiveNullChecks.kt") public void testPrimitiveNullChecks() throws Exception {