Introduce a separate StackValue class for lateinit local vars
Local lateinit var differs in behavior from a simple local var, and logic that relies of 'instanceof Local' checks assuming that all instances of Local are simple local vars can produce faulty code (as in KT-23260, where a local lateinit var was not explicitly put on stack when passed as an argument to an inline function, thus causing null propagation). #KT-23260 Fixed Target versions 1.2.50
This commit is contained in:
@@ -157,8 +157,13 @@ public abstract class StackValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Local local(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor) {
|
public static StackValue local(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor) {
|
||||||
return new Local(index, type, descriptor.getType(), descriptor.isLateInit(), descriptor.getName());
|
if (descriptor.isLateInit()) {
|
||||||
|
return new LateinitLocal(index, type, descriptor.getType(), descriptor.getName());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new Local(index, type, descriptor.getType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -805,50 +810,65 @@ public abstract class StackValue {
|
|||||||
|
|
||||||
public static class Local extends StackValue {
|
public static class Local extends StackValue {
|
||||||
public final int index;
|
public final int index;
|
||||||
private final boolean isLateinit;
|
|
||||||
private final Name name;
|
|
||||||
|
|
||||||
private Local(int index, Type type, KotlinType kotlinType, boolean isLateinit, Name name) {
|
private Local(int index, Type type, KotlinType kotlinType) {
|
||||||
super(type, kotlinType, false);
|
super(type, kotlinType, false);
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
throw new IllegalStateException("local variable index must be non-negative");
|
throw new IllegalStateException("local variable index must be non-negative");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLateinit && name == null) {
|
|
||||||
throw new IllegalArgumentException("Lateinit local variable should have name: #" + index + " " + type.getDescriptor());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.isLateinit = isLateinit;
|
|
||||||
this.name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Local(int index, Type type) {
|
private Local(int index, Type type) {
|
||||||
this(index, type, null, false, null);
|
this(index, type, null);
|
||||||
}
|
|
||||||
|
|
||||||
private Local(int index, Type type, KotlinType kotlinType) {
|
|
||||||
this(index, type, kotlinType, false, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
|
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
|
||||||
v.load(index, this.type);
|
v.load(index, this.type);
|
||||||
if (isLateinit) {
|
|
||||||
StackValue.genNonNullAssertForLateinit(v, name.asString());
|
|
||||||
}
|
|
||||||
coerceTo(type, kotlinType, v);
|
coerceTo(type, kotlinType, v);
|
||||||
// TODO unbox
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
|
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
|
||||||
coerceFrom(topOfStackType, topOfStackKotlinType, v);
|
coerceFrom(topOfStackType, topOfStackKotlinType, v);
|
||||||
v.store(index, this.type);
|
v.store(index, this.type);
|
||||||
if (isLateinit) {
|
}
|
||||||
PseudoInsnsKt.storeNotNull(v);
|
}
|
||||||
|
|
||||||
|
public static class LateinitLocal extends StackValue {
|
||||||
|
public final int index;
|
||||||
|
private final Name name;
|
||||||
|
|
||||||
|
private LateinitLocal(int index, Type type, KotlinType kotlinType, Name name) {
|
||||||
|
super(type, kotlinType, false);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
throw new IllegalStateException("local variable index must be non-negative");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name == null) {
|
||||||
|
throw new IllegalArgumentException("Lateinit local variable should have name: #" + index + " " + type.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.index = index;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
|
||||||
|
v.load(index, this.type);
|
||||||
|
StackValue.genNonNullAssertForLateinit(v, name.asString());
|
||||||
|
coerceTo(type, kotlinType, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
|
||||||
|
coerceFrom(topOfStackType, topOfStackKotlinType, v);
|
||||||
|
v.store(index, this.type);
|
||||||
|
PseudoInsnsKt.storeNotNull(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
lateinit var str: String
|
||||||
|
try {
|
||||||
|
println(str)
|
||||||
|
return "Should throw an exception"
|
||||||
|
}
|
||||||
|
catch (e: UninitializedPropertyAccessException) {
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
catch (e: Throwable) {
|
||||||
|
return "Unexpected exception: ${e::class}"
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+6
@@ -13812,6 +13812,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt");
|
runTest("compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt23260.kt")
|
||||||
|
public void testKt23260() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/local/kt23260.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("localLateinit.kt")
|
@TestMetadata("localLateinit.kt")
|
||||||
public void testLocalLateinit() throws Exception {
|
public void testLocalLateinit() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt");
|
runTest("compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt");
|
||||||
|
|||||||
+6
@@ -13812,6 +13812,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt");
|
runTest("compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt23260.kt")
|
||||||
|
public void testKt23260() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/local/kt23260.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("localLateinit.kt")
|
@TestMetadata("localLateinit.kt")
|
||||||
public void testLocalLateinit() throws Exception {
|
public void testLocalLateinit() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt");
|
runTest("compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt");
|
||||||
|
|||||||
+6
@@ -13812,6 +13812,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt");
|
runTest("compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt23260.kt")
|
||||||
|
public void testKt23260() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/local/kt23260.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("localLateinit.kt")
|
@TestMetadata("localLateinit.kt")
|
||||||
public void testLocalLateinit() throws Exception {
|
public void testLocalLateinit() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt");
|
runTest("compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt");
|
||||||
|
|||||||
+6
@@ -13202,6 +13202,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt");
|
runTest("compiler/testData/codegen/box/properties/lateinit/local/capturedLocalLateinit.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt23260.kt")
|
||||||
|
public void testKt23260() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/local/kt23260.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("localLateinit.kt")
|
@TestMetadata("localLateinit.kt")
|
||||||
public void testLocalLateinit() throws Exception {
|
public void testLocalLateinit() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt");
|
runTest("compiler/testData/codegen/box/properties/lateinit/local/localLateinit.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user