aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYohann Roussel <yroussel@google.com>2017-09-05 11:27:34 +0200
committerYohann Roussel <yroussel@google.com>2017-09-05 11:27:34 +0200
commit3efd0605da491fe7161c962b2a0fd8e32167292c (patch)
tree7d8209d4c98131ffb3cbdbf0c66f548cf848fc3a
parent625b2409796742626c3ff6d273ede1ac4355dc6e (diff)
downloadr8-3efd0605da491fe7161c962b2a0fd8e32167292c.tar.gz
Loosen desugaring constraints
And enable all desugaring by by default. Loosening as defined in the discussion document: - Skip desugaring of anything if the target method is defined in the bootclasspath (i.e. API android.jar). - Just warn about invokestatic, invokesuper and call to method handle when the target class or interface is missing. - Change handling of hierarchy: - If all declared interfaces (and their super interfaces hierarchies) of a class are known and the class has an implementation for all default methods found in the declared interface of the class (even those hidden in subinterfaces), skip default method desugaring for the class. - In any other case of missing element in the hierachy is reported as a warning. missing interfaces are ignored, missing class cause the subclasses to be ignored during default method desugaring. Change-Id: Ided965d8dbceac4d494d82ff6426dd35a2cbe0a4
-rw-r--r--src/main/java/com/android/tools/r8/D8Command.java2
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java151
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java14
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java84
-rw-r--r--src/main/java/com/android/tools/r8/utils/InternalOptions.java4
-rw-r--r--src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java100
-rw-r--r--src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java17
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java10
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java10
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java13
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java9
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java13
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java10
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java21
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java10
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java21
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java18
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java32
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java13
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java14
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java32
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java15
-rw-r--r--src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java32
-rw-r--r--src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java10
-rw-r--r--src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java5
-rw-r--r--src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java8
-rw-r--r--src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java481
-rw-r--r--src/test/java/com/android/tools/r8/R8RunArtTestsTest.java26
-rw-r--r--src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java13
-rw-r--r--src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java4
-rw-r--r--src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java148
-rw-r--r--src/test/java/com/android/tools/r8/ToolHelper.java4
-rw-r--r--src/test/java/com/android/tools/r8/debug/DebugTestBase.java1
-rw-r--r--src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java22
34 files changed, 1225 insertions, 142 deletions
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 5b2e5cfb3..76779bb1b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -217,8 +217,6 @@ public class D8Command extends BaseCompilerCommand {
internal.skipMinification = true;
assert internal.useTreeShaking;
internal.useTreeShaking = false;
- assert internal.interfaceMethodDesugaring == OffOrAuto.Off;
- assert internal.tryWithResourcesDesugaring == OffOrAuto.Off;
assert internal.inlineAccessors;
internal.inlineAccessors = false;
assert internal.removeSwitchMaps;
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 b7036f521..d3964d1d1 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
@@ -15,6 +15,8 @@ import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
@@ -42,9 +44,7 @@ final class ClassProcessor {
}
final void process(DexClass clazz) {
- if (clazz.isInterface()) {
- throw new CompilationError("Interface in superclass chain.");
- }
+ assert !clazz.isInterface();
if (!clazz.isProgramClass()) {
// We assume that library classes don't need to be processed, since they
// are provided by a runtime not supporting default interface methods.
@@ -61,8 +61,15 @@ final class ClassProcessor {
// about methods added to superclasses when we decide if we want to add a default
// method to class `clazz`.
DexType superType = clazz.superType;
- if (superType != null && superType != rewriter.factory.objectType) {
- process(rewriter.findRequiredClass(superType));
+ // If superClass definition is missing, just skip this part and let real processing of its
+ // subclasses report the error if it is required.
+ DexClass superClass = superType == null ? null : rewriter.findDefinitionFor(superType);
+ if (superClass != null && superType != rewriter.factory.objectType) {
+ if (superClass.isInterface()) {
+ throw new CompilationError("Interface `" + superClass.toSourceString()
+ + "` used as super class of `" + clazz.toSourceString() + "`.");
+ }
+ process(superClass);
}
if (clazz.interfaces.isEmpty()) {
@@ -95,7 +102,10 @@ final class ClassProcessor {
private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) {
DexMethod method = defaultMethod.method;
- assert !rewriter.findRequiredClass(method.holder).isLibraryClass();
+ // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
+ // even if this results in invalid code, these classes are never desugared.
+ assert rewriter.findDefinitionFor(method.holder) != null
+ && !rewriter.findDefinitionFor(method.holder).isLibraryClass();
// New method will have the same name, proto, and also all the flags of the
// default method, including bridge flag.
DexMethod newMethod = rewriter.factory.createMethod(clazz.type, method.proto, method.name);
@@ -113,23 +123,62 @@ final class ClassProcessor {
// in this class.
private List<DexEncodedMethod> collectMethodsToImplement(DexClass clazz) {
DefaultMethodsHelper helper = new DefaultMethodsHelper();
-
+ DexClass current = clazz;
+ List<DexEncodedMethod> accumulatedVirtualMethods = new ArrayList<>();
// Collect candidate default methods by inspecting interfaces implemented
// by this class as well as its superclasses.
- DexClass current = clazz;
- while (true) {
+ //
+ // We assume here that interfaces implemented by java.lang.Object don't
+ // have default methods to desugar since they are library interfaces. And we assume object
+ // methods don't hide any default interface methods. Default interface method matching Object's
+ // methods is supposed to fail with a compilation error.
+ // Note that this last assumption will be broken if Object API is augmented with a new method in
+ // the future.
+ while (current.type != rewriter.factory.objectType) {
for (DexType type : current.interfaces.values) {
- helper.merge(getOrCreateInterfaceInfo(type));
+ helper.merge(getOrCreateInterfaceInfo(clazz, current, type));
}
- DexType superType = current.superType;
- if (superType == null || superType == rewriter.factory.objectType) {
- // We assume here that interfaces implemented by java.lang.Object don't
- // have default methods and don't hide any default interface methods since
- // they must be library interfaces.
+ accumulatedVirtualMethods.addAll(Arrays.asList(clazz.virtualMethods()));
+
+ List<DexEncodedMethod> defaultMethodsInDirectInterface = helper.createFullList();
+ List<DexEncodedMethod> toBeImplementedFromDirectInterface =
+ new ArrayList<>(defaultMethodsInDirectInterface.size());
+ hideCandidates(accumulatedVirtualMethods,
+ defaultMethodsInDirectInterface,
+ toBeImplementedFromDirectInterface);
+ // toBeImplementedFromDirectInterface are those that we know for sure we need to implement by
+ // looking at the already desugared super classes.
+ // Remaining methods in defaultMethodsInDirectInterface are those methods we need to look at
+ // the hierarchy to know how they should be handled.
+ if (toBeImplementedFromDirectInterface.isEmpty()
+ && defaultMethodsInDirectInterface.isEmpty()) {
+ // No interface with default in direct hierarchy, nothing to do: super already has all that
+ // is needed.
+ return Collections.emptyList();
+ }
+
+ if (current.superType == null) {
break;
+ } else {
+ DexClass superClass = rewriter.findDefinitionFor(current.superType);
+ if (superClass != null) {
+ current = superClass;
+ } else {
+ String message = "Default method desugaring of `" + clazz.toSourceString() + "` failed";
+ if (current == clazz) {
+ message += " because its super class `" + clazz.superType.toSourceString()
+ + "` is missing";
+ } else {
+ message +=
+ " because it's hierarchy is incomplete. The class `"
+ + current.superType.toSourceString()
+ + "` is missing and it is the declared super class of `"
+ + current.toSourceString() + "`";
+ }
+ throw new CompilationError(message);
+ }
}
- current = rewriter.findRequiredClass(superType);
}
List<DexEncodedMethod> candidates = helper.createCandidatesList();
@@ -142,25 +191,41 @@ final class ClassProcessor {
current = clazz;
while (true) {
// Hide candidates by virtual method of the class.
- hideCandidates(current.virtualMethods(), candidates, toBeImplemented);
+ hideCandidates(Arrays.asList(current.virtualMethods()), candidates, toBeImplemented);
if (candidates.isEmpty()) {
return toBeImplemented;
}
DexType superType = current.superType;
- if (superType == null || superType == rewriter.factory.objectType) {
+ DexClass superClass = null;
+ if (superType != null) {
+ superClass = rewriter.findDefinitionFor(superType);
+ // It's available or we would have failed while analyzing the hierarchy for interfaces.
+ assert superClass != null;
+ }
+ if (superClass == null || superType == rewriter.factory.objectType) {
// Note that default interface methods must never have same
// name/signature as any method in java.lang.Object (JLS ยง9.4.1.2).
// Everything still in candidate list is not hidden.
toBeImplemented.addAll(candidates);
+ // NOTE: we also intentionally remove 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 on older platforms!
+ toBeImplemented.removeIf(
+ method -> {
+ DexClass holder = rewriter.findDefinitionFor(method.method.holder);
+ // Holder of a found method to implement is a defined interface.
+ assert holder != null;
+ return holder.isLibraryClass();
+ });
return toBeImplemented;
}
- current = rewriter.findRequiredClass(superType);
+ current = superClass;
}
}
- private void hideCandidates(DexEncodedMethod[] virtualMethods,
+ private void hideCandidates(List<DexEncodedMethod> virtualMethods,
List<DexEncodedMethod> candidates, List<DexEncodedMethod> toBeImplemented) {
Iterator<DexEncodedMethod> it = candidates.iterator();
while (it.hasNext()) {
@@ -188,42 +253,56 @@ final class ClassProcessor {
}
}
- private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(DexType iface) {
+ private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(
+ DexClass classToDesugar,
+ DexClass implementing,
+ DexType iface) {
DefaultMethodsHelper.Collection collection = cache.get(iface);
if (collection != null) {
return collection;
}
- collection = createInterfaceInfo(iface);
+ collection = createInterfaceInfo(classToDesugar, implementing, iface);
cache.put(iface, collection);
return collection;
}
- private DefaultMethodsHelper.Collection createInterfaceInfo(DexType iface) {
+ private DefaultMethodsHelper.Collection createInterfaceInfo(
+ DexClass classToDesugar,
+ DexClass implementing,
+ DexType iface) {
DefaultMethodsHelper helper = new DefaultMethodsHelper();
- DexClass clazz = rewriter.findRequiredClass(iface);
- if (!clazz.isInterface()) {
- throw new CompilationError(
- "Type " + iface.toSourceString() + " is expected to be an interface.");
+ DexClass definedInterface = rewriter.findDefinitionFor(iface);
+ if (definedInterface == null) {
+ String message = "Interface `" + iface.toSourceString()
+ + "` not found. It's needed to make sure desugaring of `"
+ + classToDesugar.toSourceString() + "` is correct. Desugaring will assume that this"
+ + " interface has no default method";
+ if (classToDesugar != implementing) {
+ message += ". This missing interface is declared in the direct hierarchy of `"
+ + implementing.toString() + "`";
+ }
+ rewriter.warnMissingClass(iface, message);
+ return helper.wrapInCollection();
}
- if (clazz.isLibraryClass()) {
- // For library interfaces we always assume there are no default
- // methods, since the interface is part of framework provided by
- // runtime which does not support default interface methods.
- return DefaultMethodsHelper.Collection.EMPTY;
+
+ 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 : clazz.interfaces.values) {
- helper.merge(getOrCreateInterfaceInfo(superinterface));
+ for (DexType superinterface : definedInterface.interfaces.values) {
+ helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
}
// Hide by virtual methods of this interface.
- for (DexEncodedMethod virtual : clazz.virtualMethods()) {
+ for (DexEncodedMethod virtual : definedInterface.virtualMethods()) {
helper.hideMatches(virtual.method);
}
// Add all default methods of this interface.
- for (DexEncodedMethod encoded : clazz.virtualMethods()) {
+ for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
if (rewriter.isDefaultMethod(encoded)) {
helper.addDefaultMethod(encoded);
}
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 2f25b1972..ae9a2ae9f 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
@@ -6,8 +6,10 @@ package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
@@ -102,6 +104,18 @@ final class DefaultMethodsHelper {
return candidates;
}
+ final List<DexEncodedMethod> createFullList() {
+ if (candidates.isEmpty() && hidden.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List<DexEncodedMethod> fullList =
+ new ArrayList<DexEncodedMethod>(candidates.size() + hidden.size());
+ fullList.addAll(candidates);
+ fullList.addAll(hidden);
+ return fullList;
+ }
+
// Create default interface collection based on collected information.
final Collection wrapInCollection() {
candidates.removeAll(hidden);
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 cbeda1e66..aa93aa8e0 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
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
@@ -25,6 +26,7 @@ import com.android.tools.r8.ir.code.InstructionListIterator;
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.logging.Log;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.ListIterator;
@@ -68,6 +70,11 @@ public final class InterfaceMethodRewriter {
// to this collection since it is only filled in ClassProcessor running synchronously.
private final Set<DexEncodedMethod> forwardingMethods = Sets.newIdentityHashSet();
+ /**
+ * A set of dexitems we have reported missing to dedupe warnings.
+ */
+ private Set<DexItem> reportedMissing = Sets.newIdentityHashSet();
+
/** Defines a minor variation in desugaring. */
public enum Flavor {
/** Process all application resources. */
@@ -100,10 +107,11 @@ public final class InterfaceMethodRewriter {
// Check that static interface methods are not referenced
// from invoke-custom instructions via method handles.
DexCallSite callSite = instruction.asInvokeCustom().getCallSite();
- reportStaticInterfaceMethodHandle(callSite.bootstrapMethod);
+ reportStaticInterfaceMethodHandle(encodedMethod.method, callSite.bootstrapMethod);
for (DexValue arg : callSite.bootstrapArgs) {
if (arg instanceof DexValue.DexValueMethodHandle) {
- reportStaticInterfaceMethodHandle(((DexValue.DexValueMethodHandle) arg).value);
+ reportStaticInterfaceMethodHandle(encodedMethod.method,
+ ((DexValue.DexValueMethodHandle) arg).value);
}
}
continue;
@@ -112,7 +120,17 @@ public final class InterfaceMethodRewriter {
if (instruction.isInvokeStatic()) {
InvokeStatic invokeStatic = instruction.asInvokeStatic();
DexMethod method = invokeStatic.getInvokedMethod();
- if (isInterfaceClass(method.holder)) {
+ DexClass clazz = findDefinitionFor(method.holder);
+ if (clazz == null) {
+ // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
+ // exception but we can not report it as error since it can also be the intended
+ // behavior.
+ warnMissingClass(encodedMethod.method, method.holder);
+ } else if (clazz.isInterface() && !clazz.isLibraryClass()) {
+ // NOTE: we intentionally don't desugar static calls into static interface
+ // methods 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 on older platforms!
// Retarget call to an appropriate method of companion class.
instructions.replaceCurrentInstruction(
new InvokeStatic(staticAsMethodOfCompanionClass(method),
@@ -124,7 +142,17 @@ public final class InterfaceMethodRewriter {
if (instruction.isInvokeSuper()) {
InvokeSuper invokeSuper = instruction.asInvokeSuper();
DexMethod method = invokeSuper.getInvokedMethod();
- if (isInterfaceClass(method.holder)) {
+ DexClass clazz = findDefinitionFor(method.holder);
+ if (clazz == null) {
+ // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
+ // exception but we can not report it as error since it can also be the intended
+ // behavior.
+ warnMissingClass(encodedMethod.method, method.holder);
+ } else if (clazz != null && (clazz.isInterface() && !clazz.isLibraryClass())) {
+ // NOTE: we intentionally don't desugar super calls into interface methods
+ // 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 on older platforms!
// Retarget call to an appropriate method of companion class.
instructions.replaceCurrentInstruction(
new InvokeStatic(defaultAsMethodOfCompanionClass(method),
@@ -135,25 +163,27 @@ public final class InterfaceMethodRewriter {
}
}
- private void reportStaticInterfaceMethodHandle(DexMethodHandle handle) {
- if (handle.type.isInvokeStatic() && isInterfaceClass(handle.asMethod().holder)) {
- throw new Unimplemented(
- "Desugaring of static interface method handle in is not yet supported.");
+ private void reportStaticInterfaceMethodHandle(DexMethod referencedFrom, DexMethodHandle handle) {
+ if (handle.type.isInvokeStatic()) {
+ DexClass holderClass = findDefinitionFor(handle.asMethod().holder);
+ // NOTE: If the class definition is missing we can't check. Let it be handled as any other
+ // missing call target.
+ if (holderClass == null) {
+ warnMissingClass(referencedFrom, handle.asMethod().holder);
+ } else if (holderClass.isInterface()) {
+ throw new Unimplemented(
+ "Desugaring of static interface method handle as in `"
+ + referencedFrom.toSourceString() + "` in is not yet supported.");
+ }
}
}
- private boolean isInterfaceClass(DexType type) {
- return findRequiredClass(type).isInterface();
- }
-
- // Returns the class for the type specified, report errors for missing classes.
- final DexClass findRequiredClass(DexType type) {
- DexClass clazz = converter.appInfo.definitionFor(type);
- if (clazz != null) {
- return clazz;
- }
- throw new CompilationError("Type '" + type.toSourceString() +
- "' required for default and static interface methods desugaring not found.");
+ /**
+ * Returns the class definition for the specified type.
+ * @return may return null if no definition for the given type is available.
+ */
+ final DexClass findDefinitionFor(DexType type) {
+ return converter.appInfo.definitionFor(type);
}
// Gets the companion class for the interface `type`.
@@ -262,4 +292,18 @@ public final class InterfaceMethodRewriter {
}
return true;
}
+
+ void warnMissingClass(DexType missing, String message) {
+ // TODO replace by a proper warning mechanic (see b/65154707).
+ // TODO think about using a common deduplicating mechanic with Enqueuer
+ if (reportedMissing.add(missing)) {
+ System.err.println(message);
+ }
+ }
+
+ private void warnMissingClass(DexItem referencedFrom, DexType clazz) {
+ warnMissingClass(clazz,
+ "Type `" + clazz.toSourceString() + "` was not found, it is required for default or"
+ + " static interface methods desugaring of `" + referencedFrom.toSourceString() + "`");
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index c241bd714..f27998cb6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -84,9 +84,9 @@ public class InternalOptions {
public List<String> logArgumentsFilter = ImmutableList.of();
// Defines interface method rewriter behavior.
- public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Off;
+ public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Auto;
// Defines try-with-resources rewriter behavior.
- public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Off;
+ public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Auto;
// Application writing mode.
public OutputMode outputMode = OutputMode.Indexed;
diff --git a/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java b/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java
new file mode 100644
index 000000000..bec4e6731
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2017, 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 desugaringwithandroidjar25;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Predicate;
+
+public class DefaultMethodInAndroidJar25 {
+ public static void main(String[] args) throws Exception {
+ ClassWithDefaultPlatformMethods.test();
+ }
+}
+
+class ClassWithDefaultPlatformMethods implements Collection<String> {
+ private final ArrayList<String> list =
+ new ArrayList<String>() {{
+ add("First");
+ add("Second");
+ }};
+
+ static void test() {
+ ClassWithDefaultPlatformMethods instance = new ClassWithDefaultPlatformMethods();
+ instance.forEach(x -> System.out.println("BEFORE: " + x));
+ instance.removeIf(x -> true);
+ instance.forEach(x -> System.out.println("AFTER: " + x));
+ }
+
+ @Override
+ public int size() {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ return list.iterator();
+ }
+
+ @Override
+ public Object[] toArray() {
+ throw new AssertionError();
+ }
+
+ @Override
+ public <T> T[] toArray(T[] a) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean add(String s) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return list.remove(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends String> c) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public void clear() {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean removeIf(Predicate<? super String> filter) {
+ return Collection.super.removeIf(filter);
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java b/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java
new file mode 100644
index 000000000..f07ea8faf
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, 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 desugaringwithandroidjar25;
+
+import java.util.Comparator;
+
+public class StaticMethodInAndroidJar25 {
+ public static void main(String[] args) throws Exception {
+ Comparator<String> comparing =
+ Comparator.comparing(x -> x, String::compareTo);
+ System.out.println("'a' <> 'b' = " + comparing.compare("a", "b"));
+ System.out.println("'b' <> 'b' = " + comparing.compare("b", "b"));
+ System.out.println("'c' <> 'b' = " + comparing.compare("c", "b"));
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java
new file mode 100644
index 000000000..98504bd45
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib1;
+
+public interface A {
+ default String foo() {
+ return "A";
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java
new file mode 100644
index 000000000..ff42039b6
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib1;
+
+public interface A2 {
+ default String foo() {
+ return "A2";
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java
new file mode 100644
index 000000000..1ab424e77
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib2;
+
+import desugaringwithmissingclasslib1.A;
+
+public interface B extends A {
+ @Override
+ default String foo() {
+ return "B";
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java
new file mode 100644
index 000000000..029f9f4cd
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib3;
+
+import desugaringwithmissingclasslib1.A;
+
+public class C implements A {
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java
new file mode 100644
index 000000000..b2aee3bac
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib4;
+
+import desugaringwithmissingclasslib3.C;
+
+public class C2 extends C {
+ public String foo2() {
+ return "C2";
+ }
+}
+
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java
new file mode 100644
index 000000000..5f3c1c2a0
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest1;
+
+import desugaringwithmissingclasslib1.A;
+import desugaringwithmissingclasslib2.B;
+
+public class ImplementMethodsWithDefault implements A, B {
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java
new file mode 100644
index 000000000..411a0162e
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest1;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+ try {
+ String foo = instance.foo();
+ if (foo.equals("B")) {
+ System.out.println("OK");
+ } else {
+ System.out.println("NOT OK: " + foo);
+ }
+ } catch (Throwable t) {
+ System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+ t.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java
new file mode 100644
index 000000000..f2ef1b623
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest2;
+
+import desugaringwithmissingclasslib2.B;
+import desugaringwithmissingclasslib3.C;
+
+public class ImplementMethodsWithDefault extends C implements B {
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java
new file mode 100644
index 000000000..c42ac8127
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest2;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+ try {
+ String foo = instance.foo();
+ if (foo.equals("B")) {
+ System.out.println("OK");
+ } else {
+ System.out.println("NOT OK: " + foo);
+ }
+ } catch (Throwable t) {
+ System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+ t.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java
new file mode 100644
index 000000000..482623705
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest3;
+
+import desugaringwithmissingclasslib2.B;
+import desugaringwithmissingclasslib3.C;
+
+public class ImplementMethodsWithDefault extends C implements B {
+ @Override
+ public String foo() {
+ return "ImplementMethodsWithDefault";
+ }
+
+ public String getB() {
+ return B.super.foo();
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java
new file mode 100644
index 000000000..bde4d09e9
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest3;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+ try {
+ String b = instance.getB();
+ if (b.equals("B")) {
+ System.out.println("OK");
+ } else {
+ System.out.println("NOT OK: " + b);
+ }
+ } catch (Throwable t) {
+ System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+ t.printStackTrace();
+ }
+ try {
+ String foo = instance.foo();
+ if (foo.equals("ImplementMethodsWithDefault")) {
+ System.out.println("OK");
+ } else {
+ System.out.println("NOT OK: " + foo);
+ }
+ } catch (Throwable t) {
+ System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+ t.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java
new file mode 100644
index 000000000..cea63246d
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest4;
+
+import desugaringwithmissingclasslib3.C;
+
+public class C2 extends C {
+ public String foo2() {
+ return "C2";
+ }
+}
+
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java
new file mode 100644
index 000000000..c62bc2cf6
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest4;
+
+import desugaringwithmissingclasslib1.A;
+import desugaringwithmissingclasslib1.A2;
+
+public class ImplementMethodsWithDefault extends C2 implements A, A2 {
+ @Override
+ public String foo() {
+ return "ImplementMethodsWithDefault";
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java
new file mode 100644
index 000000000..e439c5c3f
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest4;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+ try {
+ String foo = instance.foo();
+ if (foo.equals("ImplementMethodsWithDefault")) {
+ System.out.println("OK");
+ } else {
+ System.out.println("NOT OK: " + foo);
+ }
+ } catch (Throwable t) {
+ System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+ t.printStackTrace();
+ }
+ try {
+ String foo = instance.foo2();
+ if (foo.equals("C2")) {
+ System.out.println("OK");
+ } else {
+ System.out.println("NOT OK: " + foo);
+ }
+ } catch (Throwable t) {
+ System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+ t.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java
new file mode 100644
index 000000000..f8dfc3bca
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest5;
+
+import desugaringwithmissingclasslib1.A;
+import desugaringwithmissingclasslib1.A2;
+import desugaringwithmissingclasslib4.C2;
+
+public class ImplementMethodsWithDefault extends C2 implements A, A2 {
+ @Override
+ public String foo() {
+ return "ImplementMethodsWithDefault";
+ }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java
new file mode 100644
index 000000000..d1ee997dc
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest5;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+ try {
+ String foo = instance.foo();
+ if (foo.equals("ImplementMethodsWithDefault")) {
+ System.out.println("OK");
+ } else {
+ System.out.println("NOT OK: " + foo);
+ }
+ } catch (Throwable t) {
+ System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+ t.printStackTrace();
+ }
+ try {
+ String foo = instance.foo2();
+ if (foo.equals("C2")) {
+ System.out.println("OK");
+ } else {
+ System.out.println("NOT OK: " + foo);
+ }
+ } catch (Throwable t) {
+ System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+ t.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 803d76022..2159d6b83 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -38,14 +38,14 @@ import org.junit.Test;
public abstract class D8IncrementalRunExamplesAndroidOTest
extends RunExamplesAndroidOTest<D8Command.Builder> {
- abstract class D8IncrementalTestRunner extends TestRunner {
+ abstract class D8IncrementalTestRunner extends TestRunner<D8IncrementalTestRunner> {
D8IncrementalTestRunner(String testName, String packageName, String mainClass) {
super(testName, packageName, mainClass);
}
@Override
- TestRunner withMinApiLevel(int minApiLevel) {
+ D8IncrementalTestRunner withMinApiLevel(int minApiLevel) {
return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
}
@@ -154,12 +154,11 @@ public abstract class D8IncrementalRunExamplesAndroidOTest
builder = transformation.apply(builder);
}
builder = builder.setOutputMode(outputMode);
- builder = builder.addLibraryFiles(
- Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
if (output != null) {
builder = builder.setOutputPath(output);
}
- addLibraryReference(builder, Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+ addLibraryReference(builder, Paths.get(ToolHelper.getAndroidJar(
+ androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
D8Command command = builder.build();
try {
return ToolHelper.runD8(command, this::combinedOptionConsumer);
@@ -288,6 +287,7 @@ public abstract class D8IncrementalRunExamplesAndroidOTest
Assert.assertArrayEquals(expectedFileNames, dexFiles);
}
+ @Override
abstract D8IncrementalTestRunner test(String testName, String packageName, String mainClass);
static byte[] readFromResource(Resource resource) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index d7076148d..c2ac969a7 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -52,6 +52,11 @@ public class D8LazyRunExamplesAndroidOTest
builder.addLibraryResourceProvider(
PreloadedClassFileProvider.fromArchive(location));
}
+
+ @Override
+ D8LazyTestRunner self() {
+ return this;
+ }
}
@Override
diff --git a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
index 5b2977c47..99fe81cfe 100644
--- a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
@@ -23,7 +23,13 @@ public class D8NonLazyRunExamplesAndroidOTest
@Override
void addLibraryReference(D8Command.Builder builder, Path location) throws IOException {
- builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+ builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(
+ androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
+ }
+
+ @Override
+ D8LazyTestRunner self() {
+ return this;
}
}
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index 6f516242d..3f5879927 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -4,33 +4,58 @@
package com.android.tools.r8;
+import static com.android.tools.r8.dex.Constants.ANDROID_K_API;
+import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.utils.OffOrAuto;
+import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.UnaryOperator;
+import org.hamcrest.core.CombinableMatcher;
+import org.hamcrest.core.IsInstanceOf;
+import org.hamcrest.core.StringContains;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.internal.matchers.ThrowableMessageMatcher;
public class D8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command.Builder> {
- class D8TestRunner extends TestRunner {
+ class D8TestRunner extends TestRunner<D8TestRunner> {
D8TestRunner(String testName, String packageName, String mainClass) {
super(testName, packageName, mainClass);
}
@Override
- TestRunner withMinApiLevel(int minApiLevel) {
+ D8TestRunner withMinApiLevel(int minApiLevel) {
return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
}
+ D8TestRunner withClasspath(Path... classpath) {
+ return withBuilderTransformation(b -> {
+ try {
+ return b.addClasspathFiles(classpath);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ });
+ }
+
+
@Override
void build(Path inputFile, Path out) throws Throwable {
D8Command.Builder builder = D8Command.builder();
for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
builder = transformation.apply(builder);
}
- builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+ builder.addLibraryFiles(
+ Paths.get(
+ ToolHelper.getAndroidJar(
+ androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
try {
ToolHelper.runD8(command, this::combinedOptionConsumer);
@@ -40,10 +65,458 @@ public class D8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command
throw re.getCause() == null ? re : re.getCause();
}
}
+
+ @Override
+ D8TestRunner self() {
+ return this;
+ }
+ }
+
+ @Test
+ public void testDefaultInInterfaceWithoutDesugaring() throws Throwable {
+ // lib1: interface A { default String foo() { return "A"; } }
+ D8TestRunner lib1 =
+ test("testDefaultInInterfaceWithoutDesugaring", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Off)
+ .withMinApiLevel(ANDROID_K_API);
+ try {
+ lib1.build();
+
+ // compilation should have failed on CompilationError since A is declaring a default method.
+ Assert.fail();
+ } catch (CompilationError | CompilationException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void testMissingInterfaceDesugared() throws Throwable {
+ // lib1: interface A { default String foo() { return "A"; } }
+ D8TestRunner lib1 =
+ test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(ANDROID_K_API);
+ lib1.build();
+
+ // lib2: interface B extends A { default String foo() { return "B"; } }
+ // lib2 is compiled with full classpath
+ D8TestRunner lib2 =
+ test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(ANDROID_K_API);
+ lib2.build();
+
+ // test: class ImplementMethodsWithDefault implements A, B {} should get its foo implementation
+ // from B.
+ // test is compiled with incomplete classpath: lib2 is missing so ImplementMethodsWithDefault is
+ // missing one of it interfaces.
+ D8TestRunner test =
+ test("desugaringwithmissingclasstest1", "desugaringwithmissingclasstest1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(ANDROID_K_API);
+ test.build();
+
+ // TODO check compilation warnings are correctly reported
+ // B is missing so compiled code makes no sense, no need to test execution.
+ }
+
+ @Test
+ public void testMissingInterfaceDesugared2AndroidK() throws Throwable {
+ int minApi = ANDROID_K_API;
+
+ // lib1: interface A { default String foo() { return "A"; } }
+ D8TestRunner lib1 =
+ test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi);
+ Path lib1Dex = lib1.build();
+
+ // lib2: interface B extends A { default String foo() { return "B"; } }
+ // lib2 is compiled with full classpath
+ D8TestRunner lib2 =
+ test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib2Dex = lib2.build();
+
+ // lib3: class C implements A {}
+ // lib3 is compiled with full classpath
+ D8TestRunner lib3 =
+ test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib3Dex = lib3.build();
+
+ // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+ // implementation from B.
+ // test is compiled with incomplete classpath: lib2 and lib3 are missing so
+ // ImplementMethodsWithDefault is missing all its hierarchy.
+ D8TestRunner test =
+ test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path testDex = test.build();
+ // TODO check compilation warnings are correctly reported
+
+ // Missing interface B is causing the wrong code to be executed.
+ thrown.expect(AssertionError.class);
+ execute(
+ "testMissingInterfaceDesugared2AndroidK",
+ "desugaringwithmissingclasstest2.Main",
+ new Path[] {
+ lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+ },
+ new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
}
+ @Test
+ public void testMissingInterfaceDesugared2AndroidO() throws Throwable {
+ int minApi = ANDROID_O_API;
+ // lib1: interface A { default String foo() { return "A"; } }
+ D8TestRunner lib1 =
+ test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi);
+ Path lib1Dex = lib1.build();
+
+ // lib2: interface B extends A { default String foo() { return "B"; } }
+ // lib2 is compiled with full classpath
+ D8TestRunner lib2 =
+ test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib2Dex = lib2.build();
+
+ // lib3: class C implements A {}
+ // lib3 is compiled with full classpath
+ D8TestRunner lib3 =
+ test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib3Dex = lib3.build();
+
+ // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+ // implementation from B.
+ // test is compiled with incomplete classpath: lib2 and lib3 are missing so
+ // ImplementMethodsWithDefault is missing all its hierarchy.
+ D8TestRunner test =
+ test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path testDex = test.build();
+ execute(
+ "testMissingInterfaceDesugared2AndroidO",
+ "desugaringwithmissingclasstest2.Main",
+ new Path[] {
+ lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+ },
+ new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+ }
+
+ @Test
+ public void testCallToMissingSuperInterfaceDesugaredAndroidK() throws Throwable {
+
+ int minApi = ANDROID_K_API;
+ // lib1: interface A { default String foo() { return "A"; } }
+ D8TestRunner lib1 =
+ test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi);
+ Path lib1Dex = lib1.build();
+
+ // lib2: interface B extends A { default String foo() { return "B"; } }
+ // lib2 is compiled with full classpath
+ D8TestRunner lib2 =
+ test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib2Dex = lib2.build();
+
+ // lib3: class C implements A {}
+ // lib3 is compiled with full classpath
+ D8TestRunner lib3 =
+ test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib3Dex = lib3.build();
+
+ // test: class ImplementMethodsWithDefault extends C implements B
+ // { String getB() { return B.super.foo(); }
+ // Should be able to call implementation from B.
+ // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing.
+ D8TestRunner test =
+ test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar(), lib3.getInputJar())
+ .withMinApiLevel(minApi);
+ Path testDex = test.build();
+ // TODO check compilation warnings are correctly reported
+
+ // Missing interface B is causing the wrong method to be executed.
+ thrown.expect(AssertionError.class);
+ execute(
+ "testCallToMissingSuperInterfaceDesugaredAndroidK",
+ "desugaringwithmissingclasstest3.Main",
+ new Path[] {
+ lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+ },
+ new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+ }
+
+ @Test
+ public void testCallToMissingSuperInterfaceDesugaredAndroidO() throws Throwable {
+ int minApi = ANDROID_O_API;
+ // lib1: interface A { default String foo() { return "A"; } }
+ D8TestRunner lib1 =
+ test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi);
+ Path lib1Dex = lib1.build();
+
+ // lib2: interface B extends A { default String foo() { return "B"; } }
+ // lib2 is compiled with full classpath
+ D8TestRunner lib2 =
+ test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib2Dex = lib2.build();
+
+ // lib3: class C implements A {}
+ // lib3 is compiled with full classpath
+ D8TestRunner lib3 =
+ test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib3Dex = lib3.build();
+
+ // test: class ImplementMethodsWithDefault extends C implements B
+ // { String getB() { return B.super.foo(); }
+ // Should be able to call implementation from B.
+ // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing.
+ D8TestRunner test =
+ test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar(), lib3.getInputJar())
+ .withMinApiLevel(minApi);
+ Path testDex = test.build();
+ execute(
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "desugaringwithmissingclasstest3.Main",
+ new Path[] {
+ lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+ },
+ new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+ }
+
+ @Test
+ public void testMissingSuperDesugaredAndroidK() throws Throwable {
+ int minApi = ANDROID_K_API;
+
+ // lib1: interface A { default String foo() { return "A"; } }
+ D8TestRunner lib1 =
+ test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi);
+ lib1.build();
+
+ // lib2: interface B extends A { default String foo() { return "B"; } }
+ // lib2 is compiled with full classpath
+ D8TestRunner lib2 =
+ test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ lib2.build();
+
+ // lib3: class C implements A {}
+ // lib3 is compiled with full classpath
+ D8TestRunner lib3 =
+ test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ lib3.build();
+
+ // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+ // implementation from B.
+ // test is compiled with incomplete classpath: lib3 is missing so
+ // ImplementMethodsWithDefault is missing its super class.
+ D8TestRunner test =
+ test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withClasspath(lib2.getInputJar())
+ .withMinApiLevel(minApi);
+ thrown.expect(
+ new CombinableMatcher<CompilationError>(new IsInstanceOf(CompilationError.class))
+ .and(new ThrowableMessageMatcher<CompilationError>(
+ new StringContains("desugaringwithmissingclasstest2.ImplementMethodsWithDefault")))
+ .and(new ThrowableMessageMatcher<CompilationError>(
+ new StringContains("desugaringwithmissingclasslib3.C"))));
+ test.build();
+ }
+
+ @Test
+ public void testMissingSuperDesugaredAndroidO() throws Throwable {
+ int minApi = ANDROID_O_API;
+
+ // lib1: interface A { default String foo() { return "A"; } }
+ D8TestRunner lib1 =
+ test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi);
+ Path lib1Dex = lib1.build();
+
+ // lib2: interface B extends A { default String foo() { return "B"; } }
+ // lib2 is compiled with full classpath
+ D8TestRunner lib2 =
+ test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib2Dex = lib2.build();
+
+ // lib3: class C implements A {}
+ // lib3 is compiled with full classpath
+ D8TestRunner lib3 =
+ test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib3Dex = lib3.build();
+
+ // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+ // implementation from B.
+ // test is compiled with incomplete classpath: lib3 is missing so
+ // ImplementMethodsWithDefault is missing its super class.
+ D8TestRunner test =
+ test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withClasspath(lib2.getInputJar())
+ .withMinApiLevel(minApi);
+ Path testDex = test.build();
+
+ execute(
+ "testMissingSuperDesugaredAndroidO",
+ "desugaringwithmissingclasstest2.Main",
+ new Path[] {
+ lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+ },
+ new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+ }
+
+ @Test
+ public void testMissingSuperDesugaredWithProgramCrossImplementationAndroidK() throws Throwable {
+ int minApi = ANDROID_K_API;
+
+ // lib1: interface A { default String foo() { return "A"; } }
+ // interface A2 { default String foo2() { return "A2"; } }
+ D8TestRunner lib1 =
+ test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi);
+ Path lib1Dex = lib1.build();
+
+ // lib3: class C { /* content irrelevant }
+ // lib3 is compiled with full classpath
+ D8TestRunner lib3 =
+ test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib3Dex = lib3.build();
+
+ // test: class C2 extends C { public String foo2() { return "C2"; } }
+ // class ImplementMethodsWithDefault extends C2 implements A, A2 {
+ // public String foo() { return "ImplementMethodsWithDefault"; }
+ // }
+ // test is compiled with incomplete classpath: lib3 is missing so
+ // C2 is missing its super class. But desugaring should be OK since all
+ // interface methods are explicitly defined in program classes of the hierarchy.
+ D8TestRunner test =
+ test("desugaringwithmissingclasstest4", "desugaringwithmissingclasstest4", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path testDex = test.build();
+
+ execute(
+ "testMissingSuperDesugaredAndroidKWithCrossImplementation",
+ "desugaringwithmissingclasstest4.Main",
+ new Path[] {
+ lib1.getInputJar(), lib3.getInputJar(), test.getInputJar()
+ },
+ new Path[] {lib1Dex, lib3Dex, testDex});
+
+ }
+
+ @Test
+ public void testMissingSuperDesugaredWithClasspathCrossImplementationAndroidK() throws Throwable {
+ int minApi = ANDROID_K_API;
+
+ // lib1: interface A { default String foo() { return "A"; } }
+ // interface A2 { default String foo2() { return "A2"; } }
+ D8TestRunner lib1 =
+ test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi);
+ Path lib1Dex = lib1.build();
+
+ // lib3: class C { /* content irrelevant }
+ // lib3 is compiled with full classpath
+ D8TestRunner lib3 =
+ test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib3Dex = lib3.build();
+
+ // lib4: class C2 extends C { public String foo2() { return "C2"; } }
+ // lib4 is compiled with full classpath
+ D8TestRunner lib4 =
+ test("desugaringwithmissingclasslib4", "desugaringwithmissingclasslib4", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar(), lib3.getInputJar())
+ .withMinApiLevel(minApi);
+ Path lib4Dex = lib4.build();
+
+ // test: class ImplementMethodsWithDefault extends C2 implements A, A2 {
+ // public String foo() { return "ImplementMethodsWithDefault"; }
+ // }
+ // test is compiled with incomplete classpath: lib3 is missing so
+ // C2 is missing its super class. But desugaring should be OK since all
+ // interface methods are explicitly defined in program classes of the hierarchy.
+ D8TestRunner test =
+ test("desugaringwithmissingclasstest4", "desugaringwithmissingclasstest4", "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withClasspath(lib1.getInputJar(), lib4.getInputJar())
+ .withMinApiLevel(minApi);
+ Path testDex = test.build();
+
+ execute(
+ "testMissingSuperDesugaredAndroidKWithCrossImplementation",
+ "desugaringwithmissingclasstest4.Main",
+ new Path[] {
+ lib1.getInputJar(), lib3.getInputJar(), lib4.getInputJar(), test.getInputJar()
+ },
+ new Path[] {lib1Dex, lib3Dex, lib4Dex, testDex});
+
+ }
@Override
- TestRunner test(String testName, String packageName, String mainClass) {
+ D8TestRunner test(String testName, String packageName, String mainClass) {
return new D8TestRunner(testName, packageName, mainClass);
}
}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 8ffab3cd8..7cb47e249 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -142,21 +142,6 @@ public abstract class R8RunArtTestsTest {
.put("975-iface-private", Constants.ANDROID_N_API)
.build();
- // Tests requiring interface method desugaring because they explicitly or
- // implicitly define static or default interface methods and are enabled
- // on pre-N Android.
- private static List<String> enableInterfaceMethodDesugaring =
- ImmutableList.of(
- "563-checker-invoke-super",
- "604-hot-static-interface",
- "961-default-iface-resolution-gen",
- "963-default-range-smali",
- "965-default-verify",
- "967-default-ame",
- "969-iface-super",
- "978-virtual-interface"
- );
-
// Tests that timeout when run with Art.
private static final Multimap<String, TestCondition> timeoutOrSkipRunWithArt =
new ImmutableListMultimap.Builder<String, TestCondition>()
@@ -205,8 +190,9 @@ public abstract class R8RunArtTestsTest {
// Tests that are never compiled or run.
private static List<String> skipAltogether = ImmutableList.of(
- // This test contains an invalid type hierarchy, which we cannot currently handle.
+ // Those tests contains an invalid type hierarchy, which we cannot currently handle.
"065-mismatched-implements",
+ "066-mismatched-super",
// This test contains invalid dex code that reads uninitialized registers after an
// an instruction that would in any case throw (implicit via aget null 0).
"706-jit-skip-compilation",
@@ -1130,7 +1116,12 @@ public abstract class R8RunArtTestsTest {
Integer minSdkVersion = needMinSdkVersion.get(name);
if (minSdkVersion != null) {
builder.setMinApiLevel(minSdkVersion);
+ builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdkVersion)));
+ } else {
+ builder.addLibraryFiles(Paths.get(
+ ToolHelper.getAndroidJar(Constants.DEFAULT_ANDROID_API)));
}
+
D8Output output = D8.run(builder.build());
output.write(Paths.get(resultPath));
break;
@@ -1153,9 +1144,6 @@ public abstract class R8RunArtTestsTest {
ToolHelper.runR8(
builder.build(),
options -> {
- if (enableInterfaceMethodDesugaring.contains(name)) {
- options.interfaceMethodDesugaring = OffOrAuto.Auto;
- }
if (disableInlining) {
options.inlineAccessors = false;
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 6c45f40a4..e63b1553b 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -46,14 +46,14 @@ public class R8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<R8Command
.run();
}
- class R8TestRunner extends TestRunner {
+ class R8TestRunner extends TestRunner<R8TestRunner> {
R8TestRunner(String testName, String packageName, String mainClass) {
super(testName, packageName, mainClass);
}
@Override
- TestRunner withMinApiLevel(int minApiLevel) {
+ R8TestRunner withMinApiLevel(int minApiLevel) {
return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
}
@@ -64,16 +64,23 @@ public class R8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<R8Command
for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
builder = transformation.apply(builder);
}
+ builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(
+ androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
ToolHelper.runR8(command, this::combinedOptionConsumer);
} catch (ExecutionException e) {
throw e.getCause();
}
}
+
+ @Override
+ R8TestRunner self() {
+ return this;
+ }
}
@Override
- TestRunner test(String testName, String packageName, String mainClass) {
+ R8TestRunner test(String testName, String packageName, String mainClass) {
return new R8TestRunner(testName, packageName, mainClass);
}
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
index 7594b0cf2..9d745f623 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
@@ -133,7 +133,8 @@ public abstract class RunExamplesAndroidNTest<B> {
thrown.expect(ApiLevelException.class);
test("staticinterfacemethods-error-due-to-min-sdk", "interfacemethods",
"StaticInterfaceMethods")
- .run();
+ .withInterfaceMethodDesugaring(OffOrAuto.Off)
+ .run();
}
@Test
@@ -149,6 +150,7 @@ public abstract class RunExamplesAndroidNTest<B> {
thrown.expect(ApiLevelException.class);
test("defaultmethods-error-due-to-min-sdk", "interfacemethods",
"DefaultMethods")
+ .withInterfaceMethodDesugaring(OffOrAuto.Off)
.run();
}
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 0ca84a1b8..c442013d1 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -22,29 +22,35 @@ import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OffOrAuto;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
-public abstract class RunExamplesAndroidOTest<B> {
+public abstract class RunExamplesAndroidOTest
+ <B extends BaseCommand.Builder<? extends BaseCommand, B>> {
static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
- abstract class TestRunner {
+ abstract class TestRunner<C extends TestRunner<C>> {
final String testName;
final String packageName;
final String mainClass;
+ Integer androidJarVersion = null;
+
final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
final List<Consumer<DexInspector>> dexInspectorChecks = new ArrayList<>();
final List<UnaryOperator<B>> builderTransformations = new ArrayList<>();
@@ -55,20 +61,22 @@ public abstract class RunExamplesAndroidOTest<B> {
this.mainClass = mainClass;
}
- TestRunner withDexCheck(Consumer<DexInspector> check) {
+ abstract C self();
+
+ C withDexCheck(Consumer<DexInspector> check) {
dexInspectorChecks.add(check);
- return this;
+ return self();
}
- TestRunner withClassCheck(Consumer<FoundClassSubject> check) {
+ C withClassCheck(Consumer<FoundClassSubject> check) {
return withDexCheck(inspector -> inspector.forAllClasses(check));
}
- TestRunner withMethodCheck(Consumer<FoundMethodSubject> check) {
+ C withMethodCheck(Consumer<FoundMethodSubject> check) {
return withClassCheck(clazz -> clazz.forAllMethods(check));
}
- <T extends InstructionSubject> TestRunner withInstructionCheck(
+ <T extends InstructionSubject> C withInstructionCheck(
Predicate<InstructionSubject> filter, Consumer<T> check) {
return withMethodCheck(method -> {
if (method.isAbstract()) {
@@ -81,22 +89,22 @@ public abstract class RunExamplesAndroidOTest<B> {
});
}
- TestRunner withOptionConsumer(Consumer<InternalOptions> consumer) {
+ C withOptionConsumer(Consumer<InternalOptions> consumer) {
optionConsumers.add(consumer);
- return this;
+ return self();
}
- TestRunner withInterfaceMethodDesugaring(OffOrAuto behavior) {
+ C withInterfaceMethodDesugaring(OffOrAuto behavior) {
return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
}
- TestRunner withTryWithResourcesDesugaring(OffOrAuto behavior) {
+ C withTryWithResourcesDesugaring(OffOrAuto behavior) {
return withOptionConsumer(o -> o.tryWithResourcesDesugaring = behavior);
}
- TestRunner withBuilderTransformation(UnaryOperator<B> builderTransformation) {
+ C withBuilderTransformation(UnaryOperator<B> builderTransformation) {
builderTransformations.add(builderTransformation);
- return this;
+ return self();
}
void combinedOptionConsumer(InternalOptions options) {
@@ -105,13 +113,25 @@ public abstract class RunExamplesAndroidOTest<B> {
}
}
+ Path build() throws Throwable {
+ Path inputFile = getInputJar();
+ Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+ build(inputFile, out);
+ return out;
+ }
+
+ Path getInputJar() {
+ return Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+ }
+
void run() throws Throwable {
if (minSdkErrorExpected(testName)) {
thrown.expect(ApiLevelException.class);
}
String qualifiedMainClass = packageName + "." + mainClass;
- Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+ Path inputFile = getInputJar();
Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
build(inputFile, out);
@@ -120,11 +140,6 @@ public abstract class RunExamplesAndroidOTest<B> {
return;
}
- boolean expectedToFail = expectedToFail(testName);
- if (expectedToFail) {
- thrown.expect(Throwable.class);
- }
-
if (!dexInspectorChecks.isEmpty()) {
DexInspector inspector = new DexInspector(out);
for (Consumer<DexInspector> check : dexInspectorChecks) {
@@ -132,21 +147,16 @@ public abstract class RunExamplesAndroidOTest<B> {
}
}
- String output = ToolHelper.runArtNoVerificationErrors(out.toString(), qualifiedMainClass);
- if (!expectedToFail) {
- ToolHelper.ProcessResult javaResult =
- ToolHelper.runJava(ImmutableList.of(inputFile.toString()), qualifiedMainClass);
- assertEquals("JVM run failed", javaResult.exitCode, 0);
- assertTrue(
- "JVM output does not match art output.\n\tjvm: "
- + javaResult.stdout
- + "\n\tart: "
- + output,
- output.equals(javaResult.stdout));
- }
+ execute(testName, qualifiedMainClass, new Path[]{inputFile}, new Path[]{out});
}
- abstract TestRunner withMinApiLevel(int minApiLevel);
+ abstract C withMinApiLevel(int minApiLevel);
+
+ C withAndroidJar(int androidJarVersion) {
+ assert this.androidJarVersion == null;
+ this.androidJarVersion = androidJarVersion;
+ return self();
+ }
abstract void build(Path inputFile, Path out) throws Throwable;
}
@@ -164,7 +174,12 @@ public abstract class RunExamplesAndroidOTest<B> {
// Dex version not supported
"invokepolymorphic",
"invokecustom",
- "invokecustom2"
+ "invokecustom2",
+ "DefaultMethodInAndroidJar25",
+ "StaticMethodInAndroidJar25",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
),
DexVm.ART_5_1_1, ImmutableList.of(
// API not supported
@@ -173,7 +188,12 @@ public abstract class RunExamplesAndroidOTest<B> {
// Dex version not supported
"invokepolymorphic",
"invokecustom",
- "invokecustom2"
+ "invokecustom2",
+ "DefaultMethodInAndroidJar25",
+ "StaticMethodInAndroidJar25",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
),
DexVm.ART_6_0_1, ImmutableList.of(
// API not supported
@@ -182,7 +202,11 @@ public abstract class RunExamplesAndroidOTest<B> {
// Dex version not supported
"invokepolymorphic",
"invokecustom",
- "invokecustom2"
+ "invokecustom2",
+ "DefaultMethodInAndroidJar25",
+ "StaticMethodInAndroidJar25",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO"
),
DexVm.ART_7_0_0, ImmutableList.of(
// API not supported
@@ -190,7 +214,10 @@ public abstract class RunExamplesAndroidOTest<B> {
// Dex version not supported
"invokepolymorphic",
"invokecustom",
- "invokecustom2"
+ "invokecustom2",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
),
DexVm.ART_DEFAULT, ImmutableList.of(
)
@@ -266,6 +293,24 @@ public abstract class RunExamplesAndroidOTest<B> {
}
@Test
+ public void desugarDefaultMethodInAndroidJar25() throws Throwable {
+ test("DefaultMethodInAndroidJar25", "desugaringwithandroidjar25", "DefaultMethodInAndroidJar25")
+ .withMinApiLevel(ANDROID_K_API)
+ .withAndroidJar(ANDROID_O_API)
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .run();
+ }
+
+ @Test
+ public void desugarStaticMethodInAndroidJar25() throws Throwable {
+ test("StaticMethodInAndroidJar25", "desugaringwithandroidjar25", "StaticMethodInAndroidJar25")
+ .withMinApiLevel(ANDROID_K_API)
+ .withAndroidJar(ANDROID_O_API)
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .run();
+ }
+
+ @Test
public void lambdaDesugaringValueAdjustments() throws Throwable {
test("lambdadesugaring-value-adjustments", "lambdadesugaring", "ValueAdjustments")
.withMinApiLevel(ANDROID_K_API)
@@ -315,5 +360,34 @@ public abstract class RunExamplesAndroidOTest<B> {
.run();
}
- abstract TestRunner test(String testName, String packageName, String mainClass);
+ abstract RunExamplesAndroidOTest<B>.TestRunner<?> test(String testName, String packageName, String mainClass);
+
+ void execute(
+ String testName,
+ String qualifiedMainClass, Path[] jars, Path[] dexes)
+ throws IOException {
+
+ boolean expectedToFail = expectedToFail(testName);
+ if (expectedToFail) {
+ thrown.expect(Throwable.class);
+ }
+ String output = ToolHelper.runArtNoVerificationErrors(
+ Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
+ qualifiedMainClass,
+ null);
+ if (!expectedToFail) {
+ ToolHelper.ProcessResult javaResult =
+ ToolHelper.runJava(
+ Arrays.stream(jars).map(path -> path.toString()).collect(Collectors.toList()),
+ qualifiedMainClass);
+ assertEquals("JVM run failed", javaResult.exitCode, 0);
+ assertTrue(
+ "JVM output does not match art output.\n\tjvm: "
+ + javaResult.stdout
+ + "\n\tart: "
+ + output,
+ output.equals(javaResult.stdout));
+ }
+ }
+
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 4f8430326..3767286bc 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -14,15 +14,11 @@ import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
-import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.shaking.RootSetBuilder;
-import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index fa78fd03c..c89e79227 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -135,6 +135,7 @@ public abstract class DebugTestBase {
.setOutputPath(dexOutputDir)
.setMinApiLevel(minSdk)
.setMode(CompilationMode.DEBUG)
+ .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
.build(),
optionsConsumer);
return dexOutputDir.resolve("classes.dex");
diff --git a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
index b6debff76..39d90d64d 100644
--- a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
@@ -45,14 +45,6 @@ public class BasicTestDependenciesDesugaringTest {
}
private static Set<String> knownIssues = Sets.newHashSet(new String[]{
- "espresso-core-3.0.0.jar",
- "hamcrest-integration-1.3.jar",
- "hamcrest-library-1.3.jar",
- "junit-4.12.jar",
- "support-core-ui-25.4.0.jar",
- "support-media-compat-25.4.0.jar",
- "support-fragment-25.4.0.jar",
- "support-compat-25.4.0.jar"
});
@Rule
@@ -98,4 +90,18 @@ public class BasicTestDependenciesDesugaringTest {
.build(),
options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
}
+
+ @Test
+ public void testCompileDontDesugarDefault() throws IOException, CompilationException {
+ if (knownIssues.contains(name)) {
+ thrown.expect(CompilationError.class);
+ }
+ ToolHelper.runD8(
+ D8Command.builder().addClasspathFiles(classpath)
+ .addProgramFiles(toCompile)
+ .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(Constants.ANDROID_K_API)))
+ .setMinApiLevel(Constants.ANDROID_K_API)
+ .build(),
+ options -> options.interfaceMethodDesugaring = OffOrAuto.Off);
+ }
}