From 4f8ef8c315bd6ebc2f03c56ea5f1e9c088f7b079 Mon Sep 17 00:00:00 2001 From: Kristoffer Andersen Date: Mon, 17 Jan 2022 16:59:25 +0100 Subject: [PATCH] [EE-IR] Support Local Functions This commit introduces support for calling and referencing local functions and objects in evaluate expression on the IR backend. The primary incision is a lowering inserted after Local Declaration Lowering, that uses the intermediate data structures recorded by LDL to rewrite calls to local functions to the appropriate function in the binary, instead of predicting the compilation strategy. The required changes to the rest of the pipeline facilitate piping the required data around. The key to this transformation is that _captures by the local function_ must be introduced as _captures by the fragment function_, such that the evaluator infrastructure can find the appropriate values at run-time. This is necessary due to the strategy of compiling local functions to static functions instead of closures. Additional test coverage of stepping behavior support the corresponding changes in the Evaluator, part of the Kotlin Debugger plug-in. --- .../kotlin/codegen/state/GenerationState.kt | 9 +- .../FirLocalVariableTestGenerated.java | 22 ++++ .../codegen/FirSteppingTestGenerated.java | 18 +++ .../common/lower/LocalDeclarationsLowering.kt | 21 +++- .../kotlin/backend/jvm/JvmIrCodegenFactory.kt | 6 +- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 90 ++++++++++---- .../FragmentLocalFunctionPatchLowering.kt | 116 ++++++++++++++++++ .../backend/jvm/lower/ReflectiveAccess.kt | 51 +++++--- .../kotlin/backend/jvm/JvmBackendContext.kt | 13 ++ .../psi2ir/generators/GeneratorContext.kt | 2 +- .../FragmentCompilerSymbolTableDecorator.kt | 15 ++- .../fragments/FragmentModuleGenerator.kt | 18 ++- .../constructors/multipleConstructors.kt | 38 ++++++ .../localVariables/constructors/property.kt | 14 +++ compiler/testData/debug/stepping/kt15259.kt | 24 ++++ compiler/testData/debug/stepping/kt29179.kt | 26 ++++ compiler/testData/debug/stepping/nullcheck.kt | 68 ++++++++++ .../codegen/IrLocalVariableTestGenerated.java | 22 ++++ .../codegen/IrSteppingTestGenerated.java | 18 +++ .../codegen/LocalVariableTestGenerated.java | 22 ++++ .../codegen/SteppingTestGenerated.java | 18 +++ 21 files changed, 575 insertions(+), 56 deletions(-) create mode 100644 compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FragmentLocalFunctionPatchLowering.kt create mode 100644 compiler/testData/debug/localVariables/constructors/multipleConstructors.kt create mode 100644 compiler/testData/debug/localVariables/constructors/property.kt create mode 100644 compiler/testData/debug/stepping/kt15259.kt create mode 100644 compiler/testData/debug/stepping/kt29179.kt create mode 100644 compiler/testData/debug/stepping/nullcheck.kt 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 {