Merge "ClassInliner should not remove IncompatibleClassChangeError"
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index ae57f48..e610d20 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -762,6 +762,11 @@
return;
}
+ DexClass clazz = appInfo.definitionFor(method.method.holder);
+ if (clazz == null) {
+ return;
+ }
+
boolean receiverUsedAsReturnValue = false;
boolean seenSuperInitCall = false;
for (Instruction insn : receiver.uniqueUsers()) {
@@ -773,7 +778,7 @@
if (insn.isInstanceGet() ||
(insn.isInstancePut() && insn.asInstancePut().object() == receiver)) {
DexField field = insn.asFieldInstruction().getField();
- if (field.clazz == method.method.holder) {
+ if (field.clazz == clazz.type && clazz.lookupInstanceField(field) != null) {
// Since class inliner currently only supports classes directly extending
// java.lang.Object, we don't need to worry about fields defined in superclasses.
continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 7bdff33..d7a57df 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -169,14 +169,16 @@
Map<InvokeMethodWithReceiver, InliningInfo> methodCalls = new IdentityHashMap<>();
+ DexClass definition = appInfo.definitionFor(clazz);
+
for (Instruction user : receiver.uniqueUsers()) {
// Field read/write.
if (user.isInstanceGet() ||
(user.isInstancePut() && user.asInstancePut().value() != receiver)) {
- if (user.asFieldInstruction().getField().clazz == newInstanceInsn.clazz) {
- // Eligible field read or write. Note: as long as we consider only classes eligible
- // if they directly extend java.lang.Object we don't need to check if the field
- // really exists in the class.
+ DexField field = user.asFieldInstruction().getField();
+ if (field.clazz == newInstanceInsn.clazz && definition.lookupInstanceField(field) != null) {
+ // Since class inliner currently only supports classes directly extending
+ // java.lang.Object, we don't need to worry about fields defined in superclasses.
continue;
}
return null; // Not eligible.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 5df1cee..38768cd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.ir.optimize.classinliner;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import com.android.tools.r8.OutputMode;
@@ -32,10 +34,13 @@
import com.android.tools.r8.ir.optimize.classinliner.trivial.Iface2Impl;
import com.android.tools.r8.ir.optimize.classinliner.trivial.ReferencedFields;
import com.android.tools.r8.ir.optimize.classinliner.trivial.TrivialTestClass;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.nio.file.Path;
import java.util.Collections;
@@ -178,6 +183,39 @@
assertFalse(inspector.clazz(BuildersTestClass.Pos.class).isPresent());
}
+ @Test
+ public void testErroneousInput() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+
+ ClassBuilder testClass = builder.addClass("A");
+ testClass.addStaticFinalField("f", "I", "123");
+ testClass.addDefaultConstructor();
+
+ ClassBuilder mainClass = builder.addClass("Main");
+ mainClass.addMainMethod(
+ ".limit stack 3",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " new A",
+ " dup",
+ " invokespecial A/<init>()V",
+ " getfield A/f I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ AndroidApp compiled =
+ compileWithR8(builder.build(), getProguardConfig(mainClass.name), this::configure);
+
+ // Check that the code fails with an IncompatibleClassChangeError with Java.
+ ProcessResult javaResult =
+ runOnJavaRaw(mainClass.name, builder.buildClasses().toArray(new byte[2][]));
+ assertThat(javaResult.stderr, containsString("IncompatibleClassChangeError"));
+
+ // Check that the code fails with an IncompatibleClassChangeError with ART.
+ ProcessResult artResult = runOnArtRaw(compiled, mainClass.name);
+ assertThat(artResult.stderr, containsString("IncompatibleClassChangeError"));
+ }
+
private Set<String> collectNewInstanceTypes(
ClassSubject clazz, String methodName, String... params) {
assertNotNull(clazz);
@@ -189,15 +227,8 @@
}
private AndroidApp runR8(AndroidApp app, Class mainClass) throws Exception {
- String config = keepMainProguardConfiguration(mainClass) + "\n"
- + "-dontobfuscate\n"
- + "-allowaccessmodification";
-
- AndroidApp compiled = compileWithR8(app, config,
- o -> {
- o.enableClassInlining = true;
- o.classInliningInstructionLimit = 10000;
- });
+ AndroidApp compiled =
+ compileWithR8(app, getProguardConfig(mainClass.getCanonicalName()), this::configure);
// Materialize file for execution.
Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
@@ -220,4 +251,16 @@
return compiled;
}
+
+ private String getProguardConfig(String main) {
+ return keepMainProguardConfiguration(main)
+ + "\n"
+ + "-dontobfuscate\n"
+ + "-allowaccessmodification";
+ }
+
+ private void configure(InternalOptions options) {
+ options.enableClassInlining = true;
+ options.classInliningInstructionLimit = 10000;
+ }
}