diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 26889498793..b26bfb6dfa1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -4039,6 +4039,9 @@ public class ExpressionCodegen extends KtVisitor impleme else if (property.hasModifier(KtTokens.LATEINIT_KEYWORD)) { initializeLocalVariable(property, null); } + else if (!property.isVar()) { + initializeLocalVariableWithFakeDefaultValue(property); + } return StackValue.none(); } @@ -4182,6 +4185,47 @@ public class ExpressionCodegen extends KtVisitor impleme storeTo.storeSelector(resultType, resultKotlinType, v); } + // This is a workaround to avoid bugs with incorrect range of uninitialized variable: + // 1) JDI error 305 + // 2) Expected R, got . in code with contracts + // 3) D8 dropping the whole LVT + private void initializeLocalVariableWithFakeDefaultValue(@NotNull KtProperty variableDeclaration) { + LocalVariableDescriptor variableDescriptor = (LocalVariableDescriptor) getVariableDescriptorNotNull(variableDeclaration); + + assert !variableDeclaration.isVar() && !variableDeclaration.hasDelegateExpressionOrInitializer() && + !variableDescriptor.isLateInit() : variableDeclaration.getText() + " in not variable declaration without initializer"; + + KotlinType kotlinType = variableDescriptor.getType(); + Type type = typeMapper.mapType(kotlinType); + + if (type == Type.VOID_TYPE) return; + + int index = lookupLocalIndex(variableDescriptor); + assert index >= 0: variableDescriptor + " is not in frame map"; + + switch (type.getSort()) { + case Type.BOOLEAN: + case Type.CHAR: + case Type.BYTE: + case Type.SHORT: + case Type.INT: + v.iconst(0); + break; + case Type.FLOAT: + v.fconst(0.0f); + break; + case Type.LONG: + v.lconst(0L); + break; + case Type.DOUBLE: + v.dconst(0.0); + break; + default: + v.aconst(null); + } + v.store(index, type); + } + @NotNull private StackValue generateProvideDelegateCallForLocalVariable( @NotNull StackValue initializer, diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxing.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxing.kt new file mode 100644 index 00000000000..dab99f34506 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxing.kt @@ -0,0 +1,12 @@ +// IGNORE_BACKEND: JVM_IR + +fun test(): java.lang.Integer { + val c: java.lang.Integer + run { + c = java.lang.Integer(1) + } + return c +} + +// 2 ASTORE 0 +// 1 LOCALVARIABLE c Ljava/lang/Integer; L1 L10 0 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/contract.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/contract.kt new file mode 100644 index 00000000000..f95119d5168 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/contract.kt @@ -0,0 +1,25 @@ +// IGNORE_BACKEND: JVM_IR + +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +@ExperimentalContracts +fun test(): Char { + val c: Char + doIt { + c = ' ' + } + return c +} + +@ExperimentalContracts +fun doIt(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +// 2 FCONST 0 +// 1 LOCALVARIABLE c Lkotlin/jvm/internal/Ref$CharRef; L1 L10 0 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/generics.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/generics.kt new file mode 100644 index 00000000000..a68fd3cb9fb --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/generics.kt @@ -0,0 +1,16 @@ +// IGNORE_BACKEND: JVM_IR + +inline fun foo(default: T): T { + val t: T + run { + t = default + } + return t +} + +fun test() { + foo(0.0f) +} + +// 2 ASTORE 1 +// 1 LOCALVARIABLE t\$iv Ljava/lang/Object; L2 L11 1 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatement.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatement.kt new file mode 100644 index 00000000000..4ed08dd034a --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatement.kt @@ -0,0 +1,16 @@ +// IGNORE_BACKEND: JVM_IR + +import kotlin.random.Random + +fun test(): Char { + val c: Char + if (Random.nextBoolean()) { + c = '1' + } else { + c = '2' + } + return c +} + +// 3 ISTORE 0 +// 1 LOCALVARIABLE c C L1 L7 0 diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatementWithoutBlock.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatementWithoutBlock.kt new file mode 100644 index 00000000000..bea5d19239d --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatementWithoutBlock.kt @@ -0,0 +1,16 @@ +// IGNORE_BACKEND: JVM_IR + +import kotlin.random.Random + +fun test(): Char { + val c: Char + if (Random.nextBoolean()) + c = '1' + else + c = '2' + + return c +} + +// 3 ISTORE 0 +// 1 LOCALVARIABLE c C L1 L5 0 diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClass.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClass.kt new file mode 100644 index 00000000000..761bdeebd53 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClass.kt @@ -0,0 +1,12 @@ +// IGNORE_BACKEND: JVM_IR + +fun test(): UInt { + val c: UInt + run { + c = 1u + } + return c +} + +// 2 ISTORE 0 +// 1 LOCALVARIABLE c I L1 L10 0 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/lateinit.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/lateinit.kt new file mode 100644 index 00000000000..fc196010485 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/lateinit.kt @@ -0,0 +1,12 @@ +// IGNORE_BACKEND: JVM_IR + +fun test(): Char { + lateinit var c: Any + run { + c = ' ' + } + return c as Char +} + +// 2 ASTORE 0 +// 1 LOCALVARIABLE c Ljava/lang/Object; L1 L11 0 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/run.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/run.kt new file mode 100644 index 00000000000..be9fa84327b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/run.kt @@ -0,0 +1,14 @@ +// IGNORE_BACKEND: JVM_IR + +fun test(): Char { + val c: Char + run { + c = ' ' + println(c) + } + return c +} + +// The first on declaration, the other on initialization +// 2 ISTORE 0 +// 1 LOCALVARIABLE c C L1 L13 0 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/singleBlock.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/singleBlock.kt new file mode 100644 index 00000000000..f194e3b83e8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/singleBlock.kt @@ -0,0 +1,23 @@ +// IGNORE_BACKEND: JVM_IR + +fun test(): Char { + val c: Char + val l = Any() + val l1 = Any() + val l2 = Any() + val l3 = Any() + val l4 = Any() + val l5 = Any() + val l6 = Any() + val l7 = Any() + val l8 = Any() + val l11 = Any() + val l12 = Any() + val l13 = Any() + val l14 = Any() + c = '1' + return c +} + +// 2 ISTORE 0 +// 1 LOCALVARIABLE c C L1 L16 0 diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/var.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/var.kt new file mode 100644 index 00000000000..27814e23522 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/var.kt @@ -0,0 +1,12 @@ +// IGNORE_BACKEND: JVM_IR + +fun test(): Char { + var c: Char + run { + c = ' ' + } + return c +} + +// 1 ASTORE 0 +// 1 LOCALVARIABLE c Lkotlin/jvm/internal/Ref\$CharRef; L1 L10 0 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/whenStatement.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/whenStatement.kt new file mode 100644 index 00000000000..b639c206434 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/whenStatement.kt @@ -0,0 +1,22 @@ +// IGNORE_BACKEND: JVM_IR + +fun test(i: Int): Char { + val c: Char + when (i) { + 1 -> c = '1' + 2 -> c = '2' + 3 -> c = '3' + 4 -> c = '4' + 5 -> c = '5' + 6 -> c = '6' + 7 -> c = '7' + 8 -> c = '8' + 9 -> c = '9' + 0 -> c = '0' + else -> c = ' ' + } + return c +} + +// 12 ISTORE 1 +// 1 LOCALVARIABLE c C L1 L16 1 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index c98f225b2fe..68701becc13 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -2585,6 +2585,74 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/localInitializationLVT") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class LocalInitializationLVT extends AbstractBytecodeTextTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInLocalInitializationLVT() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/localInitializationLVT"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("boxing.kt") + public void testBoxing() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/boxing.kt"); + } + + @TestMetadata("contract.kt") + public void testContract() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/contract.kt"); + } + + @TestMetadata("generics.kt") + public void testGenerics() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/generics.kt"); + } + + @TestMetadata("ifStatement.kt") + public void testIfStatement() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatement.kt"); + } + + @TestMetadata("ifStatementWithoutBlock.kt") + public void testIfStatementWithoutBlock() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatementWithoutBlock.kt"); + } + + @TestMetadata("inlineClass.kt") + public void testInlineClass() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClass.kt"); + } + + @TestMetadata("lateinit.kt") + public void testLateinit() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/lateinit.kt"); + } + + @TestMetadata("run.kt") + public void testRun() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/run.kt"); + } + + @TestMetadata("singleBlock.kt") + public void testSingleBlock() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/singleBlock.kt"); + } + + @TestMetadata("var.kt") + public void testVar() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/var.kt"); + } + + @TestMetadata("whenStatement.kt") + public void testWhenStatement() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/whenStatement.kt"); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/multifileClasses") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/IrBytecodeTextTestGenerated.java index 6a1a78067fe..9e5d5bfea57 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/IrBytecodeTextTestGenerated.java @@ -2585,6 +2585,74 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/localInitializationLVT") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class LocalInitializationLVT extends AbstractIrBytecodeTextTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInLocalInitializationLVT() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/localInitializationLVT"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); + } + + @TestMetadata("boxing.kt") + public void testBoxing() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/boxing.kt"); + } + + @TestMetadata("contract.kt") + public void testContract() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/contract.kt"); + } + + @TestMetadata("generics.kt") + public void testGenerics() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/generics.kt"); + } + + @TestMetadata("ifStatement.kt") + public void testIfStatement() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatement.kt"); + } + + @TestMetadata("ifStatementWithoutBlock.kt") + public void testIfStatementWithoutBlock() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/ifStatementWithoutBlock.kt"); + } + + @TestMetadata("inlineClass.kt") + public void testInlineClass() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClass.kt"); + } + + @TestMetadata("lateinit.kt") + public void testLateinit() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/lateinit.kt"); + } + + @TestMetadata("run.kt") + public void testRun() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/run.kt"); + } + + @TestMetadata("singleBlock.kt") + public void testSingleBlock() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/singleBlock.kt"); + } + + @TestMetadata("var.kt") + public void testVar() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/var.kt"); + } + + @TestMetadata("whenStatement.kt") + public void testWhenStatement() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/localInitializationLVT/whenStatement.kt"); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/multifileClasses") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)