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:
Dmitry Petrov
2018-03-15 13:52:07 +03:00
parent 7be4f5d31f
commit 22c2d8b563
6 changed files with 81 additions and 22 deletions
@@ -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}"
}
}
@@ -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");
@@ -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");
@@ -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");
@@ -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");