Update nullability tests for local type analysis.
Bug: 69964136
Change-Id: I8c6022f5d01538d8ebc3a45b33069965b06cbb0e
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index 91d53a2..2fe6685 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -67,6 +67,7 @@
// 2) It does not have an out value, so we can skip the env updating.
if (instruction instanceof Argument) {
if (argumentsSeen < 0) {
+ // Receiver
derived = TypeLatticeElement.fromDexType(appInfo, encodedMethod.method.holder, false);
} else {
DexType argType = encodedMethod.method.proto.parameters.values[argumentsSeen];
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index da8772f..d7c9132 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -7,12 +7,19 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.ArrayGet;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
@@ -21,6 +28,8 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
import java.util.function.BiConsumer;
import org.junit.Test;
@@ -46,6 +55,24 @@
inspector.accept(appInfo, analysis);
}
+ private static void verifyClassTypeLattice(
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices,
+ DexType receiverType,
+ Value v,
+ TypeLatticeElement l) {
+ assertTrue(l instanceof ClassTypeLatticeElement);
+ ClassTypeLatticeElement lattice = (ClassTypeLatticeElement) l;
+ // Receiver
+ if (lattice.classType.equals(receiverType)) {
+ assertFalse(l.isNullable());
+ } else {
+ TypeLatticeElement expected = expectedLattices.get(v.definition.getClass());
+ if (expected != null) {
+ assertEquals(expected, l);
+ }
+ }
+ }
+
@Test
public void nonNullAfterSafeInvokes() throws Exception {
SmaliBuilder builder = new SmaliBuilder(CLASS_NAME);
@@ -58,20 +85,15 @@
"throw v0",
":not_null",
"return-void"
- );
+ );
buildAndTest(builder, signature, (appInfo, typeAnalysis) -> {
- typeAnalysis.forEach((v, l) -> {
- assertTrue(l instanceof ClassTypeLatticeElement);
- ClassTypeLatticeElement lattice = (ClassTypeLatticeElement) l;
- if (lattice.classType.equals(appInfo.dexItemFactory.stringType)) {
+ DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
+ DexType example = appInfo.dexItemFactory.createType("LExample;");
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
// TODO(b/70795205): Can be refined by using control-flow info.
- // parameter
- assertTrue(lattice.isNullable());
- } else {
- // holder and newly created assertion error.
- assertFalse(lattice.isNullable());
- }
- });
+ InvokeVirtual.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
+ NewInstance.class, new ClassTypeLatticeElement(assertionErrorType, false));
+ typeAnalysis.forEach((v, l) -> verifyClassTypeLattice(expectedLattices, example, v, l));
});
}
@@ -91,19 +113,14 @@
// p1 could be still null at the outside of try-catch.
"invoke-virtual {p1}, Ljava/lang/String;->hashCode()I",
"return-void"
- );
+ );
buildAndTest(builder, signature, (appInfo, typeAnalysis) -> {
- typeAnalysis.forEach((v, l) -> {
- assertTrue(l instanceof ClassTypeLatticeElement);
- ClassTypeLatticeElement lattice = (ClassTypeLatticeElement) l;
- if (lattice.classType.equals(appInfo.dexItemFactory.stringType)) {
- // parameter
- assertTrue(lattice.isNullable());
- } else {
- // holder and newly created assertion error.
- assertFalse(lattice.isNullable());
- }
- });
+ DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
+ DexType example = appInfo.dexItemFactory.createType("LExample;");
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
+ InvokeVirtual.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
+ NewInstance.class, new ClassTypeLatticeElement(assertionErrorType, false));
+ typeAnalysis.forEach((v, l) -> verifyClassTypeLattice(expectedLattices, example, v, l));
});
}
@@ -120,24 +137,22 @@
"throw v0",
":not_null",
"return-void"
- );
+ );
buildAndTest(builder, signature, (appInfo, typeAnalysis) -> {
+ DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
+ DexType example = appInfo.dexItemFactory.createType("LExample;");
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
+ // An element inside a non-null array could be null.
+ ArrayGet.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
+ NewInstance.class, new ClassTypeLatticeElement(assertionErrorType, false));
typeAnalysis.forEach((v, l) -> {
if (l instanceof ArrayTypeLatticeElement) {
ArrayTypeLatticeElement lattice = (ArrayTypeLatticeElement) l;
assertEquals(appInfo.dexItemFactory.stringType, lattice.elementType);
// TODO(b/70795205): Can be refined by using control-flow info.
assertTrue(l.isNullable());
- }
- if (l instanceof ClassTypeLatticeElement) {
- ClassTypeLatticeElement lattice = (ClassTypeLatticeElement) l;
- if (lattice.classType.equals(appInfo.dexItemFactory.stringType)) {
- // An element inside a non-null array could be null.
- assertTrue(lattice.isNullable());
- } else {
- // holder and newly created assertion error.
- assertFalse(lattice.isNullable());
- }
+ } else if (l instanceof ClassTypeLatticeElement) {
+ verifyClassTypeLattice(expectedLattices, example, v, l);
}
});
});
@@ -160,23 +175,21 @@
// p1 could be still null at the outside of try-catch.
"invoke-virtual {p1}, [Ljava/lang/String;->hashCode()I",
"return-void"
- );
+ );
buildAndTest(builder, signature, (appInfo, typeAnalysis) -> {
+ DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
+ DexType example = appInfo.dexItemFactory.createType("LExample;");
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
+ // An element inside a non-null array could be null.
+ ArrayGet.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
+ NewInstance.class, new ClassTypeLatticeElement(assertionErrorType, false));
typeAnalysis.forEach((v, l) -> {
if (l instanceof ArrayTypeLatticeElement) {
ArrayTypeLatticeElement lattice = (ArrayTypeLatticeElement) l;
assertEquals(appInfo.dexItemFactory.stringType, lattice.elementType);
assertTrue(l.isNullable());
- }
- if (l instanceof ClassTypeLatticeElement) {
- ClassTypeLatticeElement lattice = (ClassTypeLatticeElement) l;
- if (lattice.classType.equals(appInfo.dexItemFactory.stringType)) {
- // An element inside a non-null array could be null.
- assertTrue(lattice.isNullable());
- } else {
- // holder and newly created assertion error.
- assertFalse(lattice.isNullable());
- }
+ } else if (l instanceof ClassTypeLatticeElement) {
+ verifyClassTypeLattice(expectedLattices, example, v, l);
}
});
});
@@ -186,34 +199,28 @@
public void nonNullAfterSafeFieldAccess() throws Exception {
SmaliBuilder builder = new SmaliBuilder(CLASS_NAME);
MethodSignature signature =
- builder.addInstanceMethod("void", "foo", ImmutableList.of("Test"), 1,
- "iget-object v0, p1, LTest;->bar:Ljava/lang/String;",
- // Successful field access above means p1 is not null.
- "if-nez p1, :not_null",
+ builder.addStaticMethod("void", "foo", ImmutableList.of("Test"), 1,
+ "iget-object v0, p0, LTest;->bar:Ljava/lang/String;",
+ // Successful field access above means p0 is not null.
+ "if-nez p0, :not_null",
"new-instance v0, Ljava/lang/AssertionError;",
"throw v0",
":not_null",
"return-void"
- );
+ );
builder.addClass("Test");
builder.addInstanceField("bar", "Ljava/lang/String;");
buildAndTest(builder, signature, (appInfo, typeAnalysis) -> {
+ DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
+ DexType example = appInfo.dexItemFactory.createType("LExample;");
DexType testType = appInfo.dexItemFactory.createType("LTest;");
- typeAnalysis.forEach((v, l) -> {
- assertTrue(l instanceof ClassTypeLatticeElement);
- ClassTypeLatticeElement lattice = (ClassTypeLatticeElement) l;
- if (lattice.classType.equals(appInfo.dexItemFactory.stringType)) {
- // instance may not be initialized.
- assertTrue(lattice.isNullable());
- } else if (lattice.classType.equals(testType)) {
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
// TODO(b/70795205): Can be refined by using control-flow info.
- // parameter
- assertTrue(lattice.isNullable());
- } else {
- // holder and newly created assertion error.
- assertFalse(lattice.isNullable());
- }
- });
+ Argument.class, new ClassTypeLatticeElement(testType, true),
+ // instance may not be initialized.
+ InstanceGet.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
+ NewInstance.class, new ClassTypeLatticeElement(assertionErrorType, false));
+ typeAnalysis.forEach((v, l) -> verifyClassTypeLattice(expectedLattices, example, v, l));
});
}
@@ -221,37 +228,31 @@
public void stillNullAfterExceptionCatch_iget() throws Exception {
SmaliBuilder builder = new SmaliBuilder(CLASS_NAME);
MethodSignature signature =
- builder.addInstanceMethod("void", "foo", ImmutableList.of("Test"), 1,
+ builder.addStaticMethod("void", "foo", ImmutableList.of("Test"), 1,
":try_start",
- "iget-object v0, p1, LTest;->bar:Ljava/lang/String;",
- "if-nez p1, :return",
+ "iget-object v0, p0, LTest;->bar:Ljava/lang/String;",
+ "if-nez p0, :return",
"new-instance v0, Ljava/lang/AssertionError;",
"throw v0",
":try_end",
".catch Ljava/lang/Throwable; {:try_start .. :try_end} :return",
":return",
- // p1 could be still null at the outside of try-catch.
- "invoke-virtual {p1}, LTest;->hashCode()I",
+ // p0 could be still null at the outside of try-catch.
+ "invoke-virtual {p0}, LTest;->hashCode()I",
"return-void"
- );
+ );
builder.addClass("Test");
builder.addInstanceField("bar", "Ljava/lang/String;");
buildAndTest(builder, signature, (appInfo, typeAnalysis) -> {
+ DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
+ DexType example = appInfo.dexItemFactory.createType("LExample;");
DexType testType = appInfo.dexItemFactory.createType("LTest;");
- typeAnalysis.forEach((v, l) -> {
- assertTrue(l instanceof ClassTypeLatticeElement);
- ClassTypeLatticeElement lattice = (ClassTypeLatticeElement) l;
- if (lattice.classType.equals(appInfo.dexItemFactory.stringType)) {
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
+ Argument.class, new ClassTypeLatticeElement(testType, true),
// instance may not be initialized.
- assertTrue(lattice.isNullable());
- } else if (lattice.classType.equals(testType)) {
- // parameter
- assertTrue(lattice.isNullable());
- } else {
- // holder and newly created assertion error.
- assertFalse(lattice.isNullable());
- }
- });
+ InstanceGet.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
+ NewInstance.class, new ClassTypeLatticeElement(assertionErrorType, false));
+ typeAnalysis.forEach((v, l) -> verifyClassTypeLattice(expectedLattices, example, v, l));
});
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
index 9bd8af6..c39d347 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
@@ -38,7 +38,7 @@
import com.android.tools.r8.utils.Smali;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
+import com.google.common.collect.ImmutableMap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -280,29 +280,18 @@
new MethodSignature("a", "void",
ImmutableList.of("Test", "Test", "Test", "Test")))
.getMethod();
-
DexType test = appInfo.dexItemFactory.createType("LTest;");
- Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = Maps.newHashMap();
- expectedLattices.put(ArrayLength.class, PRIMITIVE);
- expectedLattices.put(ConstNumber.class, PRIMITIVE);
- expectedLattices.put(
- ConstString.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false));
- expectedLattices.put(CheckCast.class, new ClassTypeLatticeElement(test, true));
- expectedLattices.put(NewInstance.class, new ClassTypeLatticeElement(test, false));
-
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
+ ArrayLength.class, PRIMITIVE,
+ ConstNumber.class, PRIMITIVE,
+ ConstString.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false),
+ CheckCast.class, new ClassTypeLatticeElement(test, true),
+ NewInstance.class, new ClassTypeLatticeElement(test, false));
try {
IRCode irCode = method.buildIR(TEST_OPTIONS);
TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
analysis.run();
- analysis.forEach((v, l) -> {
- if (v.definition == null) {
- return;
- }
- TypeLatticeElement expected = expectedLattices.get(v.definition.getClass());
- if (expected != null) {
- assertEquals(expected, l);
- }
- });
+ analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
} catch (ApiLevelException e) {
fail(e.getMessage());
}
@@ -316,30 +305,32 @@
.method(
new MethodSignature("onClick", "void", ImmutableList.of("Test")))
.getMethod();
-
DexType test = appInfo.dexItemFactory.createType("LTest;");
- Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = Maps.newHashMap();
- expectedLattices.put(ConstNumber.class, PRIMITIVE);
- expectedLattices.put(
- ConstString.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false));
- expectedLattices.put(InstanceOf.class, PRIMITIVE);
- expectedLattices.put(StaticGet.class, new ClassTypeLatticeElement(test, true));
-
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
+ ConstNumber.class, PRIMITIVE,
+ ConstString.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false),
+ InstanceOf.class, PRIMITIVE,
+ StaticGet.class, new ClassTypeLatticeElement(test, true));
try {
IRCode irCode = method.buildIR(TEST_OPTIONS);
TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
analysis.run();
- analysis.forEach((v, l) -> {
- if (v.definition == null) {
- return;
- }
- TypeLatticeElement expected = expectedLattices.get(v.definition.getClass());
- if (expected != null) {
- assertEquals(expected, l);
- }
- });
+ analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
} catch (ApiLevelException e) {
fail(e.getMessage());
}
}
+
+ private static void verifyTypeEnvironment(
+ Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices,
+ Value v,
+ TypeLatticeElement l) {
+ if (v.definition == null) {
+ return;
+ }
+ TypeLatticeElement expected = expectedLattices.get(v.definition.getClass());
+ if (expected != null) {
+ assertEquals(expected, l);
+ }
+ }
}