Version 1.0.22
Merge: Proper desugaring of invoke-direct calls to default interface methods.
CL: https://r8-review.googlesource.com/c/r8/+/18220
Merge: Fixing super calls to default interface methods in desugaring.
CL: https://r8-review.googlesource.com/c/r8/+/19080
Change-Id: If4285e7e1aaad0b5f085761103611c3079deb576
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 398d25d..ab1f699 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 = "v1.0.21";
+ public static final String LABEL = "v1.0.22";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index fa0958b..f042cf7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -32,8 +32,6 @@
private final Set<DexClass> processedClasses = Sets.newIdentityHashSet();
// Maps already created methods into default methods they were generated based on.
private final Map<DexEncodedMethod, DexEncodedMethod> createdMethods = new IdentityHashMap<>();
- // Caches default interface method info for already processed interfaces.
- private final Map<DexType, DefaultMethodsHelper.Collection> cache = new IdentityHashMap<>();
ClassProcessor(InterfaceMethodRewriter rewriter) {
this.rewriter = rewriter;
@@ -67,7 +65,7 @@
if (superClass != null && superType != rewriter.factory.objectType) {
if (superClass.isInterface()) {
throw new CompilationError("Interface `" + superClass.toSourceString()
- + "` used as super class of `" + clazz.toSourceString() + "`.");
+ + "` used as super class of `" + clazz.toSourceString() + "`.");
}
process(superClass);
}
@@ -136,7 +134,7 @@
// the future.
while (current.type != rewriter.factory.objectType) {
for (DexType type : current.interfaces.values) {
- helper.merge(getOrCreateInterfaceInfo(clazz, current, type));
+ helper.merge(rewriter.getOrCreateInterfaceInfo(clazz, current, type));
}
accumulatedVirtualMethods.addAll(Arrays.asList(clazz.virtualMethods()));
@@ -167,8 +165,8 @@
} else {
String message = "Default method desugaring of `" + clazz.toSourceString() + "` failed";
if (current == clazz) {
- message += " because its super class `" + clazz.superType.toSourceString()
- + "` is missing";
+ message += " because its super class `" +
+ clazz.superType.toSourceString() + "` is missing";
} else {
message +=
" because it's hierarchy is incomplete. The class `"
@@ -252,54 +250,4 @@
}
}
}
-
- private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(
- DexClass classToDesugar,
- DexClass implementing,
- DexType iface) {
- DefaultMethodsHelper.Collection collection = cache.get(iface);
- if (collection != null) {
- return collection;
- }
- collection = createInterfaceInfo(classToDesugar, implementing, iface);
- cache.put(iface, collection);
- return collection;
- }
-
- private DefaultMethodsHelper.Collection createInterfaceInfo(
- DexClass classToDesugar,
- DexClass implementing,
- DexType iface) {
- DefaultMethodsHelper helper = new DefaultMethodsHelper();
- DexClass definedInterface = rewriter.findDefinitionFor(iface);
- if (definedInterface == null) {
- rewriter.warnMissingInterface(classToDesugar, implementing, iface);
- return helper.wrapInCollection();
- }
-
- if (!definedInterface.isInterface()) {
- throw new CompilationError(
- "Type " + iface.toSourceString() + " is referenced as an interface of `"
- + implementing.toString() + "`.");
- }
-
- // Merge information from all superinterfaces.
- for (DexType superinterface : definedInterface.interfaces.values) {
- helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
- }
-
- // Hide by virtual methods of this interface.
- for (DexEncodedMethod virtual : definedInterface.virtualMethods()) {
- helper.hideMatches(virtual.method);
- }
-
- // Add all default methods of this interface.
- for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
- if (rewriter.isDefaultMethod(encoded)) {
- helper.addDefaultMethod(encoded);
- }
- }
-
- return helper.wrapInCollection();
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
index df65e7e..fb051aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
@@ -40,6 +40,22 @@
this.live = live;
this.hidden = hidden;
}
+
+ // If there is just one live method having specified
+ // signature return it, otherwise return null.
+ DexMethod getSingleCandidate(DexMethod method) {
+ DexMethod candidate = null;
+ for (DexEncodedMethod encodedMethod : live) {
+ DexMethod current = encodedMethod.method;
+ if (current.proto == method.proto && current.name == method.name) {
+ if (candidate != null) {
+ return null;
+ }
+ candidate = current;
+ }
+ }
+ return candidate;
+ }
}
final void merge(Collection collection) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 3d1f79d..9b8b8e2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeSuper;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -33,6 +34,7 @@
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
//
// Default and static interface method desugaring rewriter (note that lambda
@@ -75,6 +77,9 @@
// to this collection since it is only filled in ClassProcessor running synchronously.
private final Set<DexEncodedMethod> forwardingMethods = Sets.newIdentityHashSet();
+ // Caches default interface method info for already processed interfaces.
+ private final Map<DexType, DefaultMethodsHelper.Collection> cache = new ConcurrentHashMap<>();
+
/**
* A set of dexitems we have reported missing to dedupe warnings.
*/
@@ -166,8 +171,10 @@
// of android.jar is provided.
// WARNING: This may result in incorrect code on older platforms!
// Retarget call to an appropriate method of companion class.
+ DexMethod amendedMethod = amendDefaultMethod(
+ findDefinitionFor(encodedMethod.method.holder), method);
instructions.replaceCurrentInstruction(
- new InvokeStatic(defaultAsMethodOfCompanionClass(method),
+ new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
invokeSuper.outValue(), invokeSuper.arguments()));
}
continue;
@@ -180,9 +187,6 @@
continue;
}
- // This is a private instance method call. Note that the referenced method
- // is expected to be in the current class since it is private, but desugaring
- // may move some methods or their code into other classes.
DexClass clazz = findDefinitionFor(method.holder);
if (clazz == null) {
// Report missing class since we don't know if it is an interface.
@@ -195,9 +199,32 @@
getMethodOrigin(encodedMethod.method));
}
- instructions.replaceCurrentInstruction(
- new InvokeStatic(privateAsMethodOfCompanionClass(method),
- invokeDirect.outValue(), invokeDirect.arguments()));
+
+ // This might be either private method call, or a call to default
+ // interface method made via invoke-direct.
+ DexEncodedMethod virtualTarget = null;
+ for (DexEncodedMethod candidate : clazz.virtualMethods()) {
+ if (candidate.method == method) {
+ virtualTarget = candidate;
+ break;
+ }
+ }
+
+ if (virtualTarget != null) {
+ // This is a invoke-direct call to a virtual method.
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(defaultAsMethodOfCompanionClass(method),
+ invokeDirect.outValue(), invokeDirect.arguments()));
+
+ } else {
+ // Otherwise this must be a private instance method call. Note that the referenced
+ // method is expected to be in the current class since it is private, but desugaring
+ // may move some methods or their code into other classes.
+
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(privateAsMethodOfCompanionClass(method),
+ invokeDirect.outValue(), invokeDirect.arguments()));
+ }
}
}
}
@@ -274,6 +301,15 @@
factory.createString(prefix + method.name.toString()));
}
+ // It is possible that referenced method actually points to an interface which does
+ // not define this default methods, but inherits it. We are making our best effort
+ // to find an appropriate method, but still use the original one in case we fail.
+ private DexMethod amendDefaultMethod(DexClass classToDesugar, DexMethod method) {
+ DexMethod singleCandidate = getOrCreateInterfaceInfo(
+ classToDesugar, classToDesugar, method.holder).getSingleCandidate(method);
+ return singleCandidate != null ? singleCandidate : method;
+ }
+
// Represent a default interface method as a method of companion class.
final DexMethod defaultAsMethodOfCompanionClass(DexMethod method) {
return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX);
@@ -309,6 +345,14 @@
for (DexEncodedMethod method : forwardingMethods) {
converter.optimizeSynthesizedMethod(method);
}
+
+ // Cached data is not needed any more.
+ clear();
+ }
+
+ private void clear() {
+ this.cache.clear();
+ this.forwardingMethods.clear();
}
private static boolean shouldProcess(
@@ -406,4 +450,62 @@
DexClass clazz = converter.appInfo.definitionFor(holder);
return clazz == null ? Origin.unknown() : clazz.getOrigin();
}
+
+ final DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(
+ DexClass classToDesugar,
+ DexClass implementing,
+ DexType iface) {
+ DefaultMethodsHelper.Collection collection = cache.get(iface);
+ if (collection != null) {
+ return collection;
+ }
+ collection = createInterfaceInfo(classToDesugar, implementing, iface);
+ Collection existing = cache.putIfAbsent(iface, collection);
+ return existing != null ? existing : collection;
+ }
+
+ private DefaultMethodsHelper.Collection createInterfaceInfo(
+ DexClass classToDesugar,
+ DexClass implementing,
+ DexType iface) {
+ DefaultMethodsHelper helper = new DefaultMethodsHelper();
+ DexClass definedInterface = findDefinitionFor(iface);
+ if (definedInterface == null) {
+ warnMissingInterface(classToDesugar, implementing, iface);
+ return helper.wrapInCollection();
+ }
+
+ if (!definedInterface.isInterface()) {
+ throw new CompilationError(
+ "Type " + iface.toSourceString() + " is referenced as an interface from `"
+ + implementing.toString() + "`.");
+ }
+
+ if (definedInterface.isLibraryClass()) {
+ // NOTE: We intentionally ignore all candidates coming from android.jar
+ // since it is only possible in case v24+ version of android.jar is provided.
+ // WARNING: This may result in incorrect code if something else than Android bootclasspath
+ // classes are given as libraries!
+ return helper.wrapInCollection();
+ }
+
+ // Merge information from all superinterfaces.
+ for (DexType superinterface : definedInterface.interfaces.values) {
+ helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
+ }
+
+ // Hide by virtual methods of this interface.
+ for (DexEncodedMethod virtual : definedInterface.virtualMethods()) {
+ helper.hideMatches(virtual.method);
+ }
+
+ // Add all default methods of this interface.
+ for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
+ if (isDefaultMethod(encoded)) {
+ helper.addDefaultMethod(encoded);
+ }
+ }
+
+ return helper.wrapInCollection();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index 3b35904..c38830b 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -13,6 +13,8 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
import java.io.File;
import java.io.IOException;
@@ -20,6 +22,7 @@
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.junit.Assert;
@@ -43,6 +46,21 @@
ensureSameOutput(main, app, classes);
}
+ protected void ensureSameOutput(String main, int apiLevel, byte[]... classes)
+ throws Exception {
+ AndroidApp app = buildAndroidApp(classes);
+ Consumer<InternalOptions> setMinApiLevel = o -> o.minApiLevel = apiLevel;
+ ProcessResult javaResult = runOnJava(main, classes);
+ ProcessResult d8Result = runOnArtRaw(compileWithD8(app, setMinApiLevel), main);
+ ProcessResult r8Result = runOnArtRaw(compileWithR8(app, setMinApiLevel), main);
+ ProcessResult r8ShakenResult = runOnArtRaw(
+ compileWithR8(app, keepMainProguardConfiguration(main) + "-dontobfuscate\n",
+ setMinApiLevel), main);
+ Assert.assertEquals(javaResult.stdout, d8Result.stdout);
+ Assert.assertEquals(javaResult.stdout, r8Result.stdout);
+ Assert.assertEquals(javaResult.stdout, r8ShakenResult.stdout);
+ }
+
private void ensureSameOutput(String main, AndroidApp app, byte[]... classes)
throws IOException, CompilationException, ExecutionException, CompilationFailedException,
ProguardRuleParserException {
@@ -123,27 +141,27 @@
}
private Path writeToZip(byte[]... classes) throws IOException {
- NameExtrator nameExtrator = new NameExtrator();
File result = temp.newFile();
try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(result.toPath(),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
for (byte[] clazz : classes) {
- String name = nameExtrator.getName(clazz);
- ZipEntry zipEntry = new ZipEntry(DescriptorUtils.getPathFromJavaType(name));
- zipEntry.setSize(clazz.length);
- out.putNextEntry(zipEntry);
- out.write(clazz);
- out.closeEntry();
+ String name = loadClassFromDump(clazz).getTypeName();
+ ZipUtils.writeToZipStream(
+ out, DescriptorUtils.getPathFromJavaType(name), clazz);
}
}
return result.toPath();
}
- private static class NameExtrator extends ClassLoader {
+ private static Class loadClassFromDump(byte[] dump) {
+ return new DumpLoader().loadClass(dump);
+ }
- public String getName(byte[] clazz) {
- Class loaded = defineClass(clazz, 0, clazz.length);
- return loaded.getTypeName();
+ private static class DumpLoader extends ClassLoader {
+
+ @SuppressWarnings("deprecation")
+ Class loadClass(byte[] clazz) {
+ return defineClass(clazz, 0, clazz.length);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 6cc9bb9..b30c141 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -442,6 +442,15 @@
private static final String ANGLER_DIR = TOOLS + "/linux/art/product/angler";
private static final String ANGLER_BOOT_IMAGE = ANGLER_DIR + "/system/framework/boot.art";
+ public static byte[] getClassAsBytes(Class clazz) throws IOException {
+ String s = clazz.getSimpleName() + ".class";
+ Class outer = clazz.getEnclosingClass();
+ if (outer != null) {
+ s = outer.getSimpleName() + '$' + s;
+ }
+ return ByteStreams.toByteArray(clazz.getResourceAsStream(s));
+ }
+
public static String getArtDir(DexVm version) {
String dir = ART_DIRS.get(version);
if (dir == null) {
@@ -591,6 +600,10 @@
}
}
+ public static int getMinApiLevelForDexVm() {
+ return getMinApiLevelForDexVm(ToolHelper.getDexVm());
+ }
+
public static int getMinApiLevelForDexVm(DexVm dexVm) {
switch (dexVm.version) {
case DEFAULT:
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
new file mode 100644
index 0000000..d2cf708
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -0,0 +1,107 @@
+// 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.desugaring.interfacemethods;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.AsmTestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.VmTestRunner.IgnoreForRangeOfVmVersions;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(VmTestRunner.class)
+public class InterfaceMethodDesugaringTests extends AsmTestBase {
+
+ @Test
+ public void testInvokeSpecialToDefaultMethod() throws Exception {
+ ensureSameOutput(
+ com.android.tools.r8.desugaring.interfacemethods.test0.TestMain.class.getCanonicalName(),
+ ToolHelper.getMinApiLevelForDexVm(),
+ ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test0.TestMain.class),
+ patchInterfaceWithDefaults(ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test0.InterfaceWithDefaults.class)));
+ }
+
+ // NOTE: this particular test is working on pre-N devices since
+ // it's fixed by interface default method desugaring.
+ @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.DEFAULT)
+ @Test
+ public void testInvokeSpecialToDefaultMethodFromStatic() throws Exception {
+ ensureSameOutput(
+ com.android.tools.r8.desugaring.interfacemethods.test1.TestMain.class.getCanonicalName(),
+ ToolHelper.getMinApiLevelForDexVm(),
+ ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test1.TestMain.class),
+ patchInterfaceWithDefaults(ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test1.InterfaceWithDefaults.class)));
+ }
+
+ @Test
+ public void testInvokeSpecialToInheritedDefaultMethod() throws Exception {
+ ensureSameOutput(
+ com.android.tools.r8.desugaring.interfacemethods.test2.TestMain.class.getCanonicalName(),
+ ToolHelper.getMinApiLevelForDexVm(),
+ ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test2.TestMain.class),
+ ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test2.Test.class),
+ ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test2.LeftTest.class),
+ ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test2.RightTest.class),
+ ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test2.Test2.class));
+ }
+
+ private static class MutableInteger {
+ int value;
+ }
+
+ private byte[] patchInterfaceWithDefaults(byte[] classBytes) throws IOException {
+ MutableInteger patched = new MutableInteger();
+ try (InputStream input = new ByteArrayInputStream(classBytes)) {
+ ClassReader cr = new ClassReader(input);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(Opcodes.ASM6, cw) {
+ @Override
+ public MethodVisitor visitMethod(int access, String name,
+ String desc, String signature, String[] exceptions) {
+ MethodVisitor visitor = super.visitMethod(access, name, desc, signature, exceptions);
+ return new MethodVisitor(Opcodes.ASM6, visitor) {
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String desc, boolean itf) {
+ if (opcode == Opcodes.INVOKEINTERFACE &&
+ owner.endsWith("InterfaceWithDefaults") &&
+ name.equals("foo")) {
+ assertEquals(0, patched.value);
+ super.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, itf);
+ patched.value++;
+
+ } else {
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ }
+ }
+ };
+ }
+ }, 0);
+ assertEquals(1, patched.value);
+ return cw.toByteArray();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/InterfaceWithDefaults.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/InterfaceWithDefaults.java
new file mode 100644
index 0000000..dc671dc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/InterfaceWithDefaults.java
@@ -0,0 +1,18 @@
+// 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.desugaring.interfacemethods.test0;
+
+public interface InterfaceWithDefaults {
+ default void foo() {
+ System.out.println("InterfaceWithDefaults::foo()");
+ }
+
+ default void bar() {
+ System.out.println("InterfaceWithDefaults::bar()");
+ this.foo();
+ }
+
+ void test();
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/TestMain.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/TestMain.java
new file mode 100644
index 0000000..79ac7b0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/TestMain.java
@@ -0,0 +1,23 @@
+// 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.desugaring.interfacemethods.test0;
+
+public class TestMain implements InterfaceWithDefaults {
+ @Override
+ public void test() {
+ System.out.println("TestMain::test()");
+ this.foo();
+ this.bar();
+ }
+
+ @Override
+ public void foo() {
+ System.out.println("TestMain::foo()");
+ }
+
+ public static void main(String[] args) {
+ new TestMain().test();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/InterfaceWithDefaults.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/InterfaceWithDefaults.java
new file mode 100644
index 0000000..6f79c96
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/InterfaceWithDefaults.java
@@ -0,0 +1,18 @@
+// 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.desugaring.interfacemethods.test1;
+
+public interface InterfaceWithDefaults {
+ default void foo() {
+ System.out.println("InterfaceWithDefaults::foo()");
+ }
+
+ static void bar(InterfaceWithDefaults iface) {
+ System.out.println("InterfaceWithDefaults::bar()");
+ iface.foo();
+ }
+
+ void test();
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/TestMain.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/TestMain.java
new file mode 100644
index 0000000..e38a17d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/TestMain.java
@@ -0,0 +1,23 @@
+// 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.desugaring.interfacemethods.test1;
+
+public class TestMain implements InterfaceWithDefaults {
+ @Override
+ public void test() {
+ System.out.println("TestMain::test()");
+ this.foo();
+ InterfaceWithDefaults.bar(this);
+ }
+
+ @Override
+ public void foo() {
+ System.out.println("TestMain::foo()");
+ }
+
+ public static void main(String[] args) {
+ new TestMain().test();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/LeftTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/LeftTest.java
new file mode 100644
index 0000000..4a5b2de
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/LeftTest.java
@@ -0,0 +1,8 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface LeftTest extends Test {
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/RightTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/RightTest.java
new file mode 100644
index 0000000..6c6ea7e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/RightTest.java
@@ -0,0 +1,8 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface RightTest extends Test {
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test.java
new file mode 100644
index 0000000..1bf3295
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test.java
@@ -0,0 +1,11 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface Test {
+ default String foo(String a) {
+ return "Test::foo(" + a + ")";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test2.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test2.java
new file mode 100644
index 0000000..b076cde
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test2.java
@@ -0,0 +1,11 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface Test2 extends LeftTest, RightTest {
+ default String bar(String a) {
+ return "Test2::bar(" + LeftTest.super.foo(a) + " + " + RightTest.super.foo(a) + ")";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/TestMain.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/TestMain.java
new file mode 100644
index 0000000..ca86963
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/TestMain.java
@@ -0,0 +1,19 @@
+// 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.desugaring.interfacemethods.test2;
+
+public class TestMain implements Test2 {
+ public static void main(String... args) {
+ TestMain m = new TestMain();
+ System.out.println(m.bar("first"));
+ System.out.println(m.foo("second"));
+ System.out.println(m.fooDelegate("third"));
+ }
+
+ private String fooDelegate(String a) {
+ return "TestMain::fooDelegate(" + Test2.super.foo(a) + ")";
+ }
+}
+