Keep track of array types in OptimizationBasicInterpreter
Merging array types with different element types, for example `[Lj/l/String;` and `[Lj/l/Object;`, now produces `[Lj/l/Object;` (instead of `Lj/l/Object;`), which allows for more precise tracking of null values because we assume that AALOAD on a non-array typed value is possible only if that value is null. #KT-54802 Fixed
This commit is contained in:
+40
-11
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.optimization.common;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil;
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
|
||||
import org.jetbrains.org.objectweb.asm.Handle;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
@@ -152,13 +153,6 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
|
||||
@NotNull BasicValue value1,
|
||||
@NotNull BasicValue value2
|
||||
) throws AnalyzerException {
|
||||
if (insn.getOpcode() == Opcodes.AALOAD) {
|
||||
Type arrayType = value1.getType();
|
||||
if (arrayType != null && arrayType.getSort() == Type.ARRAY) {
|
||||
return new StrictBasicValue(AsmUtil.correctElementType(arrayType));
|
||||
}
|
||||
}
|
||||
|
||||
switch (insn.getOpcode()) {
|
||||
case IALOAD:
|
||||
case BALOAD:
|
||||
@@ -204,7 +198,13 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
|
||||
case DREM:
|
||||
return StrictBasicValue.DOUBLE_VALUE;
|
||||
case AALOAD:
|
||||
return StrictBasicValue.NULL_VALUE;
|
||||
Type arrayType = value1.getType();
|
||||
if (arrayType != null && arrayType.getSort() == Type.ARRAY) {
|
||||
return new StrictBasicValue(AsmUtil.correctElementType(arrayType));
|
||||
}
|
||||
else {
|
||||
return StrictBasicValue.NULL_VALUE;
|
||||
}
|
||||
case LCMP:
|
||||
case FCMPL:
|
||||
case FCMPG:
|
||||
@@ -359,13 +359,11 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
|
||||
return StrictBasicValue.UNINITIALIZED_VALUE;
|
||||
}
|
||||
|
||||
// if merge of two references then `lub` is java/lang/Object
|
||||
// arrays also are BasicValues with reference type's
|
||||
if (isReference(v) && isReference(w)) {
|
||||
if (v == NULL_VALUE) return newValue(w.getType());
|
||||
if (w == NULL_VALUE) return newValue(v.getType());
|
||||
|
||||
return StrictBasicValue.REFERENCE_VALUE;
|
||||
return mergeReferenceTypes(w.getType(), v.getType());
|
||||
}
|
||||
|
||||
// if merge of something can be stored in int var (int, char, boolean, byte, character)
|
||||
@@ -380,4 +378,35 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
|
||||
private static boolean isReference(@NotNull BasicValue v) {
|
||||
return v.getType().getSort() == Type.OBJECT || v.getType().getSort() == Type.ARRAY;
|
||||
}
|
||||
|
||||
// Merge reference types, keeping track of array dimensions.
|
||||
// See also org.jetbrains.org.objectweb.asm.Frame.merge.
|
||||
// It's assumed that types a and b are not equal when calling this method.
|
||||
private BasicValue mergeReferenceTypes(@NotNull Type a, @NotNull Type b) {
|
||||
// Find out the minimal array dimension of both types.
|
||||
int arrayDimensions = 0;
|
||||
while (a.getSort() == Type.ARRAY && b.getSort() == Type.ARRAY) {
|
||||
a = AsmUtil.correctElementType(a);
|
||||
b = AsmUtil.correctElementType(b);
|
||||
arrayDimensions++;
|
||||
}
|
||||
// Either of the two types is not an array -> result is j/l/Object.
|
||||
if (arrayDimensions == 0) return REFERENCE_VALUE;
|
||||
|
||||
// Both of the types are arrays, and element type of one or both of them is primitive ->
|
||||
// result is array of j/l/Object with one fewer dimension. E.g.
|
||||
// merge([I, [Lj/l/Object;) = Lj/l/Object;
|
||||
// merge([I, [S) = Lj/l/Object;
|
||||
// merge([[I, [[Lj/l/Object;) = [Lj/l/Object;
|
||||
if (AsmUtil.isPrimitive(a) || AsmUtil.isPrimitive(b)) {
|
||||
arrayDimensions--;
|
||||
}
|
||||
|
||||
// Result is array of j/l/Object with the computed dimension.
|
||||
StringBuilder result = new StringBuilder();
|
||||
while (arrayDimensions-- > 0) result.append("[");
|
||||
result.append(AsmTypes.OBJECT_TYPE.getDescriptor());
|
||||
return newValue(Type.getType(result.toString()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -4829,6 +4829,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt54802.kt")
|
||||
public void testKt54802() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaToUnitCast.kt")
|
||||
public void testLambdaToUnitCast() throws Exception {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// IGNORE_LIGHT_ANALYSIS
|
||||
|
||||
fun box(): String =
|
||||
g(arrayOf("O"))
|
||||
|
||||
@@ -12,4 +9,3 @@ inline fun <T> Array<out T>.f(lambda: (T) -> T): T =
|
||||
|
||||
inline fun <reified T> Array<out T>?.orEmpty0(): Array<out T> =
|
||||
this ?: (arrayOfNulls<T>(0) as Array<T>)
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
class K {
|
||||
val x: String = "OK"
|
||||
}
|
||||
|
||||
inline fun <T> Array<out T>.ifEmpty(body: () -> Array<out T>): Array<out T> =
|
||||
if (size == 0) body() else this
|
||||
|
||||
inline fun <T> Array<out T>.f(p: (T) -> String): String =
|
||||
p(this[0])
|
||||
|
||||
fun box(): String =
|
||||
emptyArray<K>()
|
||||
.ifEmpty { arrayOf(K()) }
|
||||
.f(K::x)
|
||||
+6
@@ -4697,6 +4697,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt54802.kt")
|
||||
public void testKt54802() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaToUnitCast.kt")
|
||||
public void testLambdaToUnitCast() throws Exception {
|
||||
|
||||
+6
@@ -4829,6 +4829,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt54802.kt")
|
||||
public void testKt54802() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaToUnitCast.kt")
|
||||
public void testLambdaToUnitCast() throws Exception {
|
||||
|
||||
+10
-5
@@ -3958,11 +3958,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Casts extends AbstractLightAnalysisModeTest {
|
||||
@TestMetadata("kt54707.kt")
|
||||
public void ignoreKt54707() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||
}
|
||||
@@ -4096,6 +4091,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/casts/kt54581.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt54707.kt")
|
||||
public void testKt54707() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt54802.kt")
|
||||
public void testKt54802() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaToUnitCast.kt")
|
||||
public void testLambdaToUnitCast() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt");
|
||||
|
||||
+6
@@ -3419,6 +3419,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt54802.kt")
|
||||
public void testKt54802() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaToUnitCast.kt")
|
||||
public void testLambdaToUnitCast() throws Exception {
|
||||
|
||||
+6
@@ -3479,6 +3479,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt54802.kt")
|
||||
public void testKt54802() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaToUnitCast.kt")
|
||||
public void testLambdaToUnitCast() throws Exception {
|
||||
|
||||
+5
@@ -3071,6 +3071,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
||||
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt54802.kt")
|
||||
public void testKt54802() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaToUnitCast.kt")
|
||||
public void testLambdaToUnitCast() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt");
|
||||
|
||||
+6
@@ -3553,6 +3553,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
|
||||
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt54802.kt")
|
||||
public void testKt54802() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaToUnitCast.kt")
|
||||
public void testLambdaToUnitCast() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user