Reproduce b/78788577.
Itf fld = new Impl();
If an implementing relation is broken (class Impl /* implements Itf */),
R8 missed Itf, resulting in a native crash on ART.
Bug: 78788577
Change-Id: Ia2458337288528a2f7d46305cf4ddc5e43d6d365
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index c359fc7..cf1454a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -834,7 +834,7 @@
return;
}
if (Log.ENABLED) {
- Log.verbose(getClass(), "Register new instatiation of `%s`.", clazz);
+ Log.verbose(getClass(), "Register new instantiation of `%s`.", clazz);
}
workList.add(Action.markInstantiated(clazz, KeepReason.instantiatedIn(method)));
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index b50718d..eb6e943 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -28,6 +28,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -86,6 +87,7 @@
private final List<String> fields = new ArrayList<>();
private boolean makeInit = false;
private boolean hasInit = false;
+ private final List<String> clinit = new ArrayList<>();
private boolean isInterface = false;
private String access = "public";
@@ -217,6 +219,10 @@
return new MethodSignature(name, returnJavaType, argumentJavaTypes);
}
+ public void addClassInitializer(String... lines) {
+ clinit.addAll(Arrays.asList(lines));
+ }
+
public FieldSignature addField(String flags, String name, String type, String value) {
fields.add(
".field " + flags + " " + name + " " + type + (value != null ? (" = " + value) : ""));
@@ -262,6 +268,11 @@
for (String method : methods) {
builder.append(method).append("\n");
}
+ if (!clinit.isEmpty()) {
+ builder.append(".method public static <clinit>()V\n");
+ clinit.forEach(line -> builder.append(line).append('\n'));
+ builder.append(".end method\n");
+ }
return builder.toString();
}
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
new file mode 100644
index 0000000..574ec6d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+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.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(VmTestRunner.class)
+public class FieldTypeTest extends TestBase {
+
+ @Ignore("b/78788577")
+ @Test
+ public void test_brokenTypeHierarchy() throws Exception {
+ JasminBuilder jasminBuilder = new JasminBuilder();
+ // interface Itf
+ ClassBuilder itf = jasminBuilder.addInterface("Itf");
+ MethodSignature foo = itf.addAbstractMethod("foo", ImmutableList.of(), "V");
+ // class Impl /* implements Itf */
+ ClassBuilder impl = jasminBuilder.addClass("Impl");
+ impl.addDefaultConstructor();
+ impl.addVirtualMethod("foo", ImmutableList.of(), "V",
+ ".limit locals 2",
+ ".limit stack 2",
+ "getstatic java/lang/System/out Ljava/io/PrintStream;",
+ "ldc \"" + "foo" + "\"",
+ "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+ "return");
+ impl.addVirtualMethod("toString", ImmutableList.of(), "Ljava/lang/String;",
+ ".limit locals 1",
+ ".limit stack 2",
+ "ldc \"" + impl.name + "\"",
+ "areturn");
+ ClassBuilder client = jasminBuilder.addClass("Client");
+ FieldSignature obj = client.addStaticFinalField("obj", itf.getDescriptor(), null);
+ client.addClassInitializer(
+ ".limit locals 1",
+ ".limit stack 2",
+ "new " + impl.name,
+ "dup",
+ "invokespecial " + impl.name + "/<init>()V",
+ "putstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
+ "return"
+ );
+
+ ClassBuilder mainClass = jasminBuilder.addClass("Main");
+ mainClass.addMainMethod(
+ ".limit locals 2",
+ ".limit stack 2",
+ "getstatic java/lang/System/out Ljava/io/PrintStream;",
+ "getstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
+ /*
+ "astore_0",
+ "aload_0",
+ // java.lang.IncompatibleClassChangeError:
+ // Class Impl does not implement the requested interface Itf
+ "invokeinterface " + itf.name + "/" + foo.name + "()V 1",
+ "aload_0",
+ */
+ "invokevirtual java/io/PrintStream/print(Ljava/lang/Object;)V",
+ "return"
+ );
+
+ final String mainClassName = mainClass.name;
+ String proguardConfig = keepMainProguardConfiguration(mainClass.name, false, false);
+
+ // Run input program on java.
+ Path outputDirectory = temp.newFolder().toPath();
+ jasminBuilder.writeClassFiles(outputDirectory);
+ ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
+ assertEquals(0, javaResult.exitCode);
+ assertThat(javaResult.stdout, containsString(impl.name));
+
+ AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
+ // Disable inlining to avoid the (short) tested method from being inlined then removed.
+ internalOptions -> internalOptions.enableInlining = false);
+
+ // Run processed (output) program on ART
+ ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
+ assertEquals(0, artResult.exitCode);
+ assertThat(artResult.stdout, containsString(impl.name));
+ assertEquals(-1, artResult.stderr.indexOf("DoFieldPut"));
+
+ DexInspector inspector = new DexInspector(processedApp);
+ ClassSubject itfSubject = inspector.clazz(itf.name);
+ assertThat(itfSubject, isPresent());
+ }
+
+}