Use interface bit in lambda target.
Bug: 166485528
Bug: 166732606
Bug: 166726895
Change-Id: Ibe8249ede4203ecbe9ae8f49fbd7ab46b4058edb
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index d203a12..9ba4398 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -33,7 +32,6 @@
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.google.common.base.Suppliers;
import com.google.common.primitives.Longs;
@@ -538,18 +536,8 @@
return accessibilityBridge;
}
- boolean holderIsInterface() {
- InternalOptions options = appView.options();
- DexMethod implMethod = descriptor.implHandle.asMethod();
- DexClass implMethodHolder = appView.definitionFor(implMethod.holder);
- if (implMethodHolder == null) {
- assert options
- .desugaredLibraryConfiguration
- .getBackportCoreLibraryMember()
- .containsKey(implMethod.holder);
- return false;
- }
- return implMethodHolder.isInterface();
+ boolean isInterface() {
+ return descriptor.implHandle.isInterface;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index a5cf917..0c64b12 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -232,7 +232,7 @@
methodToCall.proto,
argValueTypes,
argRegisters,
- target.holderIsInterface()));
+ target.isInterface()));
// Does the method have return value?
if (enforcedReturnType.isVoidType()) {
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 6f18ef1..05f7848 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1482,7 +1482,7 @@
}
Path path = temp.newFolder().toPath().resolve("classes.jar");
ArchiveConsumer consumer = new ArchiveConsumer(path);
- for (Class clazz : classes) {
+ for (Class<?> clazz : classes) {
consumer.accept(
ByteDataView.of(ToolHelper.getClassAsBytes(clazz)),
DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()),
@@ -1492,6 +1492,24 @@
return path;
}
+ public Path buildOnDexRuntime(TestParameters parameters, byte[]... classes)
+ throws IOException, CompilationFailedException {
+ if (parameters.isDexRuntime()) {
+ return testForD8()
+ .addProgramClassFileData(classes)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
+ }
+ Path path = temp.newFolder().toPath().resolve("classes.jar");
+ ArchiveConsumer consumer = new ArchiveConsumer(path);
+ for (byte[] clazz : classes) {
+ consumer.accept(ByteDataView.of(clazz), extractClassDescriptor(clazz), null);
+ }
+ consumer.finished(null);
+ return path;
+ }
+
public static String binaryName(Class<?> clazz) {
return DescriptorUtils.getBinaryNameFromJavaType(typeName(clazz));
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/StaticInterfaceMethodReferenceTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/StaticInterfaceMethodReferenceTest.java
new file mode 100644
index 0000000..627e0ae
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/StaticInterfaceMethodReferenceTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2020, 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.desugaring.interfacemethods;
+
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StaticInterfaceMethodReferenceTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean isInterface;
+
+ @Parameterized.Parameters(name = "{0}, itf:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+ BooleanUtils.values());
+ }
+
+ public StaticInterfaceMethodReferenceTest(TestParameters parameters, boolean isInterface) {
+ this.parameters = parameters;
+ this.isInterface = isInterface;
+ }
+
+ @Test
+ public void test() throws Exception {
+ TestRunResult<?> result;
+ if (parameters.isCfRuntime()) {
+ result =
+ testForJvm()
+ .addProgramClasses(TestClass.class)
+ .addProgramClassFileData(getTarget(isInterface))
+ .run(parameters.getRuntime(), TestClass.class);
+ } else {
+ result =
+ testForD8()
+ .addOptionsModification(o -> o.testing.allowInvokeErrors = true)
+ .addProgramClasses(TestClass.class)
+ .addProgramClassFileData(getTarget(isInterface))
+ .run(parameters.getRuntime(), TestClass.class);
+ }
+ checkResult(result);
+ }
+
+ private void checkResult(TestRunResult<?> result) {
+ // If the reference is of correct type, or running on JDK8 which allows mismatch the
+ // output is the expected print.
+ if (isInterface
+ || (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm() == CfVm.JDK8)) {
+ result.assertSuccessWithOutputLines("Target::foo");
+ return;
+ }
+
+ // DEX runtimes do not check method reference types so the code will run.
+ // TODO(b/166732606): Compile to an ICCE? What about the "missing" target case?
+ assertFalse(isInterface);
+ if (parameters.isDexRuntime()) {
+ result.assertSuccessWithOutputLines("Target::foo");
+ return;
+ }
+
+ // Otherwise the run should result in an ICCE.
+ result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+ }
+
+ private static byte[] getTarget(boolean isInterface) throws Exception {
+ return transformer(Target.class)
+ .setAccessFlags(
+ classAccessFlags -> {
+ if (isInterface) {
+ assert !classAccessFlags.isSuper();
+ assert classAccessFlags.isAbstract();
+ assert classAccessFlags.isInterface();
+ } else {
+ classAccessFlags.unsetAbstract();
+ classAccessFlags.unsetInterface();
+ }
+ })
+ .transform();
+ }
+
+ interface Target {
+
+ static void foo() {
+ System.out.println("Target::foo");
+ }
+ }
+
+ static class TestClass {
+
+ static void call(Runnable fn) {
+ fn.run();
+ }
+
+ public static void main(String[] args) {
+ call(Target::foo);
+ }
+ }
+}