Merge "Avoid merging lambda-instantiated interfaces"
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index d2552d2..6549848 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.3.3-dev";
+ public static final String LABEL = "1.3.4-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 44fb6b0..5fe8972 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -116,7 +116,7 @@
return Constraint.ALWAYS;
}
Collection<DexEncodedMethod> targets = lookupTargets(info, invocationContext);
- if (targets == null || targets.isEmpty()) {
+ if (targets == null) {
return Constraint.NEVER;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index de86cb4..d9fa610 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -127,8 +127,8 @@
types = new TypeVerificationHelper(code, factory, appInfo).computeVerificationTypes();
splitExceptionalBlocks();
LoadStoreHelper loadStoreHelper = new LoadStoreHelper(code, types);
- loadStoreHelper.insertLoadsAndStores();
DeadCodeRemover.removeDeadCode(code, rewriter, graphLense, options);
+ loadStoreHelper.insertLoadsAndStores();
removeUnneededLoadsAndStores();
registerAllocator = new CfRegisterAllocator(code, options);
registerAllocator.allocateRegisters();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java
new file mode 100644
index 0000000..adb87e6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java
@@ -0,0 +1,117 @@
+// 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.ir.optimize.inliner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.OutputMode;
+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.ir.optimize.inliner.interfaces.InterfaceTargetsTestClass;
+import com.android.tools.r8.ir.optimize.inliner.interfaces.InterfaceTargetsTestClass.IfaceA;
+import com.android.tools.r8.ir.optimize.inliner.interfaces.InterfaceTargetsTestClass.IfaceB;
+import com.android.tools.r8.ir.optimize.inliner.interfaces.InterfaceTargetsTestClass.IfaceC;
+import com.android.tools.r8.ir.optimize.inliner.interfaces.InterfaceTargetsTestClass.IfaceD;
+import com.android.tools.r8.ir.optimize.inliner.interfaces.InterfaceTargetsTestClass.IfaceNoImpl;
+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.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.InternalOptions;
+import java.nio.file.Path;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(VmTestRunner.class)
+public class InlinerTest extends TestBase {
+ @Test
+ public void testInterfacesWithoutTargets() throws Exception {
+ byte[][] classes = {
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.IfaceNoImpl.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.IfaceA.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.BaseA.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.DerivedA.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.IfaceB.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.BaseB.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.DerivedB.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.IfaceC.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.IfaceC2.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.BaseC.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.DerivedC.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.IfaceD.class),
+ ToolHelper.getClassAsBytes(InterfaceTargetsTestClass.BaseD.class)
+ };
+ AndroidApp app = runR8(buildAndroidApp(classes), InterfaceTargetsTestClass.class);
+
+ String javaOutput = runOnJava(InterfaceTargetsTestClass.class);
+ String artOutput = runOnArt(app, InterfaceTargetsTestClass.class);
+ assertEquals(javaOutput, artOutput);
+
+ DexInspector inspector = new DexInspector(app);
+ ClassSubject clazz = inspector.clazz(InterfaceTargetsTestClass.class);
+
+ assertFalse(getMethodSubject(clazz,
+ "testInterfaceNoImpl", String.class, IfaceNoImpl.class).isPresent());
+ assertFalse(getMethodSubject(clazz,
+ "testInterfaceA", String.class, IfaceA.class).isPresent());
+ assertFalse(getMethodSubject(clazz,
+ "testInterfaceB", String.class, IfaceB.class).isPresent());
+ assertFalse(getMethodSubject(clazz,
+ "testInterfaceD", String.class, IfaceC.class).isPresent());
+ assertFalse(getMethodSubject(clazz,
+ "testInterfaceD", String.class, IfaceD.class).isPresent());
+ }
+
+ private MethodSubject getMethodSubject(
+ ClassSubject clazz, String methodName, Class retValue, Class... params) {
+ return clazz.method(new MethodSignature(methodName, retValue.getTypeName(),
+ Stream.of(params).map(Class::getTypeName).collect(Collectors.toList())));
+ }
+
+ private AndroidApp runR8(AndroidApp app, Class mainClass) throws Exception {
+ AndroidApp compiled =
+ compileWithR8(app, getProguardConfig(mainClass.getCanonicalName()), this::configure);
+
+ // Materialize file for execution.
+ Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
+ compiled.writeToZip(generatedDexFile, OutputMode.DexIndexed);
+
+ // Run with ART.
+ String artOutput = ToolHelper.runArtNoVerificationErrors(
+ generatedDexFile.toString(), mainClass.getCanonicalName());
+
+ // Compare with Java.
+ ProcessResult javaResult = ToolHelper.runJava(
+ ToolHelper.getClassPathForTests(), mainClass.getCanonicalName());
+
+ if (javaResult.exitCode != 0) {
+ System.out.println(javaResult.stdout);
+ System.err.println(javaResult.stderr);
+ fail("JVM failed for: " + mainClass);
+ }
+ assertEquals("JVM and ART output differ", javaResult.stdout, artOutput);
+
+ return compiled;
+ }
+
+ private String getProguardConfig(String main) {
+ return keepMainProguardConfiguration(main)
+ + "\n"
+ + "-dontobfuscate\n"
+ + "-allowaccessmodification";
+ }
+
+ private void configure(InternalOptions options) {
+ options.enableClassInlining = false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfaces/InterfaceTargetsTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfaces/InterfaceTargetsTestClass.java
new file mode 100644
index 0000000..d132833
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfaces/InterfaceTargetsTestClass.java
@@ -0,0 +1,102 @@
+// 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.ir.optimize.inliner.interfaces;
+
+public class InterfaceTargetsTestClass {
+ public static void main(String[] args) {
+ System.out.println(testInterfaceNoImpl(null));
+ System.out.println(testInterfaceA(null));
+ System.out.println(testInterfaceB(null));
+ System.out.println(testInterfaceC(null));
+ System.out.println(testInterfaceD(null));
+ }
+
+ public interface IfaceNoImpl {
+ void foo();
+ }
+
+ public static String testInterfaceNoImpl(IfaceNoImpl iface) {
+ if (iface != null) {
+ iface.foo();
+ }
+ return "testInterfaceNoImpl::OK";
+ }
+
+ public interface IfaceA {
+ void foo();
+ }
+
+ public static class BaseA {
+ public void foo() {
+ }
+ }
+
+ public static class DerivedA extends BaseA implements IfaceA {
+ }
+
+ public static String testInterfaceA(IfaceA iface) {
+ if (iface != null) {
+ iface.foo();
+ }
+ return "testInterfaceA::OK";
+ }
+
+ public interface IfaceB {
+ void foo();
+ }
+
+ public abstract static class BaseB implements IfaceB {
+ }
+
+ public static class DerivedB extends BaseB {
+ public void foo() {
+ }
+ }
+
+ public static String testInterfaceB(IfaceB iface) {
+ if (iface != null) {
+ iface.foo();
+ }
+ return "testInterfaceB::OK";
+ }
+
+ public interface IfaceC {
+ void foo();
+ }
+
+ public interface IfaceC2 extends IfaceC {
+ default void foo() {
+ }
+ }
+
+ public abstract static class BaseC implements IfaceC {
+ }
+
+ public static class DerivedC extends BaseC implements IfaceC2 {
+ }
+
+ public static String testInterfaceC(IfaceC iface) {
+ if (iface != null) {
+ iface.foo();
+ }
+ return "testInterfaceC::OK";
+ }
+
+ public interface IfaceD {
+ void foo();
+ }
+
+ public static class BaseD implements IfaceD {
+ public void foo() {
+ }
+ }
+
+ public static String testInterfaceD(IfaceD iface) {
+ if (iface != null) {
+ iface.foo();
+ }
+ return "testInterfaceD::OK";
+ }
+}