Merge "Eliminate debug-moves if their preceding phi-move is sufficient."
diff --git a/build.gradle b/build.gradle
index 82feb00..d13029e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -379,6 +379,20 @@
}
}
+task CompatProguard(type: Jar) {
+ from sourceSets.main.output
+ baseName 'compatproguard'
+ manifest {
+ attributes 'Main-Class': 'com.android.tools.r8.compatproguard.CompatProguard'
+ }
+ // In order to build without dependencies, pass the exclude_deps property using:
+ // gradle -Pexclude_deps CompatProguard
+ if (!project.hasProperty('exclude_deps')) {
+ // Also include dependencies
+ from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+ }
+}
+
task D8Logger(type: Jar) {
from sourceSets.main.output
baseName 'd8logger'
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 5b2e5cf..76779bb 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 @@
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/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
new file mode 100644
index 0000000..45eb6e4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -0,0 +1,56 @@
+// 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 com.android.tools.r8.compatproguard;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+
+public class CompatProguard {
+ public static class CompatProguardOptions {
+ public final List<String> proguardConfig;
+
+ CompatProguardOptions(List<String> proguardConfig) {
+ this.proguardConfig = proguardConfig;
+ }
+
+ public static CompatProguardOptions parse(String[] args) {
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ if (args.length > 0) {
+ StringBuilder currentLine = new StringBuilder(args[0]);
+ for (int i = 1; i < args.length; i++) {
+ String arg = args[i];
+ if (arg.charAt(0) == '-') {
+ builder.add(currentLine.toString());
+ currentLine = new StringBuilder(arg);
+ } else {
+ currentLine.append(' ').append(arg);
+ }
+ }
+ builder.add(currentLine.toString());
+ }
+ return new CompatProguardOptions(builder.build());
+ }
+ }
+
+ private static void run(String[] args) throws IOException, CompilationException {
+ System.out.println("CompatProguard " + String.join(" ", args));
+ // Run R8 passing all the options from the command line as a Proguard configuration.
+ CompatProguardOptions options = CompatProguardOptions.parse(args);
+ R8.run(R8Command.builder().addProguardConfiguration(options.proguardConfig).build());
+ }
+
+ public static void main(String[] args) throws IOException {
+ try {
+ run(args);
+ } catch (CompilationException e) {
+ System.err.println(e.getMessage());
+ System.exit(1);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
index bf39f6f..23f20a2 100644
--- a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
@@ -3,9 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.DescriptorUtils;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
public class DebugLocalInfo {
+ public enum PrintLevel {
+ NONE,
+ NAME,
+ FULL
+ }
+
+ public static final PrintLevel PRINT_LEVEL = PrintLevel.NAME;
+
public final DexString name;
public final DexType type;
public final DexString signature;
@@ -58,6 +68,17 @@
@Override
public String toString() {
- return name + ":" + type + (signature == null ? "" : signature);
+ switch (PRINT_LEVEL) {
+ case NONE:
+ return "";
+ case NAME:
+ return name.toString();
+ case FULL:
+ return name + ":" + (signature == null
+ ? type
+ : DescriptorUtils.descriptorToJavaType(signature.toString()));
+ default:
+ throw new Unreachable();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index c9e26b4..2134ccf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -767,9 +768,6 @@
StringBuilder builder = new StringBuilder();
builder.append("block ");
builder.append(number);
- builder.append(" (");
- builder.append(System.identityHashCode(this));
- builder.append(')');
builder.append(", pred-counts: " + predecessors.size());
if (unfilledPredecessorsCount > 0) {
builder.append(" (" + unfilledPredecessorsCount + " unfilled)");
@@ -813,11 +811,37 @@
StringUtils.appendLeftPadded(builder, Integer.toString(instruction.getNumber()), 6);
builder.append(": ");
StringUtils.appendRightPadded(builder, instruction.toString(), 20);
- builder.append('\n');
+ if (DebugLocalInfo.PRINT_LEVEL != PrintLevel.NONE) {
+ List<Value> localEnds = new ArrayList<>(instruction.getDebugValues().size());
+ List<Value> localStarts = new ArrayList<>(instruction.getDebugValues().size());
+ List<Value> localLive = new ArrayList<>(instruction.getDebugValues().size());
+ for (Value value : instruction.getDebugValues()) {
+ if (value.getDebugLocalEnds().contains(instruction)) {
+ localEnds.add(value);
+ } else if (value.getDebugLocalStarts().contains(instruction)) {
+ localStarts.add(value);
+ } else {
+ assert value.debugUsers().contains(instruction);
+ localLive.add(value);
+ }
+ }
+ printDebugValueSet("live", localLive, builder);
+ printDebugValueSet("end", localEnds, builder);
+ printDebugValueSet("start", localStarts, builder);
+ }
+ builder.append("\n");
}
return builder.toString();
}
+ private void printDebugValueSet(String header, List<Value> locals, StringBuilder builder) {
+ if (!locals.isEmpty()) {
+ builder.append(" [").append(header).append(": ");
+ StringUtils.append(builder, locals, ", ", BraceType.NONE);
+ builder.append("]");
+ }
+ }
+
public void print(CfgPrinter printer) {
printer.begin("block");
printer.print("name \"B").append(number).append("\"\n");
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 65995ff..28a4d74 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -278,6 +278,40 @@
&& value.isZero();
}
+ /**
+ * Determine if the only possible values for the phi are the integers 0 or 1.
+ */
+ public boolean knownToBeBoolean() {
+ return knownToBeBoolean(new HashSet<>());
+ }
+
+ private boolean knownToBeBoolean(HashSet<Phi> active) {
+ active.add(this);
+
+ for (Value operand : operands) {
+ if (!operand.isPhi()) {
+ if (operand.isConstNumber()) {
+ ConstNumber number = operand.getConstInstruction().asConstNumber();
+ if (!number.isIntegerOne() && !number.isIntegerZero()) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ for (Value operand : operands) {
+ if (operand.isPhi() && !active.contains(operand.asPhi())) {
+ if (!operand.asPhi().knownToBeBoolean(active)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
private MoveType computeOutType(Set<Phi> active) {
if (outType != null) {
return outType;
@@ -293,7 +327,7 @@
}
// We did not find a non-phi operand that dictates the type. Recurse on phi arguments.
for (Value operand : operands) {
- if (operand.isPhi() && !active.contains(operand)) {
+ if (operand.isPhi() && !active.contains(operand.asPhi())) {
MoveType phiType = operand.asPhi().computeOutType(active);
// TODO(zerny): If we had a CONST_ZERO type element, we could often avoid going through
// all phis. We would only have to recurse until we got a non CONST_ZERO out type.
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 b7036f5..d3964d1 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.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 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 @@
// 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 @@
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 @@
// 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.
- break;
+ 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();
}
- current = rewriter.findRequiredClass(superType);
+
+ 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);
+ }
+ }
}
List<DexEncodedMethod> candidates = helper.createCandidatesList();
@@ -142,25 +191,41 @@
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 @@
}
}
- 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 2f25b19..ae9a2ae 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 @@
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 @@
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 cbeda1e..aa93aa8 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.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.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 @@
// 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 @@
// 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 @@
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 @@
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 @@
}
}
- 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 @@
}
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/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index bc93fcf..2aa9015 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -335,9 +335,10 @@
ListIterator<LocalRange> it = openRanges.listIterator(0);
Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>();
Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>();
+ int endPositionCorrection = instruction.isDebugPosition() ? 1 : 0;
while (it.hasNext()) {
LocalRange openRange = it.next();
- if (openRange.end <= index) {
+ if (openRange.end <= index - endPositionCorrection) {
it.remove();
assert currentLocals.get(openRange.register) == openRange.local;
currentLocals.remove(openRange.register);
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 c241bd7..f27998c 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 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/debugTestResources/Locals.java b/src/test/debugTestResources/Locals.java
index 5afcabb..1221234 100644
--- a/src/test/debugTestResources/Locals.java
+++ b/src/test/debugTestResources/Locals.java
@@ -318,6 +318,26 @@
nop();
}
+ public static int localConstant(boolean b) {
+ if (b) {
+ int result1 = 1;
+ return result1;
+ } else {
+ int result2 = 2;
+ return result2;
+ }
+ }
+
+ public static int localConstantBis(boolean b) {
+ int result = 0;
+ if (b) {
+ result = 1;
+ } else {
+ result = 2;
+ }
+ return result;
+ }
+
public static void main(String[] args) {
noLocals();
unusedLocals();
@@ -338,5 +358,7 @@
switchRewriteToSwitches(1);
regression65039701(true);
regression65066975(false);
+ System.out.println(localConstant(true));
+ System.out.println(localConstantBis(true));
}
}
diff --git a/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java b/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java
new file mode 100644
index 0000000..bec4e67
--- /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 0000000..f07ea8f
--- /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 0000000..98504bd
--- /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 0000000..ff42039
--- /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 0000000..1ab424e
--- /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 0000000..029f9f4
--- /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 0000000..b2aee3b
--- /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 0000000..5f3c1c2
--- /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 0000000..411a016
--- /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 0000000..f2ef1b6
--- /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 0000000..c42ac81
--- /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 0000000..4826237
--- /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 0000000..bde4d09
--- /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 0000000..cea6324
--- /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 0000000..c62bc2c
--- /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 0000000..e439c5c
--- /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 0000000..f8dfc3b
--- /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 0000000..d1ee997
--- /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/examplesAndroidO/lambdadesugaring/ValueAdjustments.java b/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
index 18f8d64..3290d86 100644
--- a/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
+++ b/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
@@ -21,7 +21,7 @@
boolean f();
}
- interface iZ {
+ interface iBoolean {
Boolean f();
}
@@ -29,15 +29,15 @@
byte f();
}
- interface iO {
+ interface iObject {
Object f();
}
- interface iN {
+ interface iNumber {
Number f();
}
- interface iB {
+ interface iByte {
Byte f();
}
@@ -45,7 +45,7 @@
char f();
}
- interface iC {
+ interface iCharacter {
Character f();
}
@@ -53,7 +53,7 @@
short f();
}
- interface iS {
+ interface iShort {
Short f();
}
@@ -61,7 +61,7 @@
int f();
}
- interface iI {
+ interface iInteger {
Integer f();
}
@@ -69,7 +69,7 @@
long f();
}
- interface iJ {
+ interface iLong {
Long f();
}
@@ -77,7 +77,7 @@
float f();
}
- interface iF {
+ interface iFloat {
Float f();
}
@@ -85,56 +85,56 @@
double f();
}
- interface iD {
+ interface iDouble {
Double f();
}
private static void checkObject(StringBuffer builder) {
builder
- .append(((iO) ValueAdjustments::z).f()).append(' ')
- .append(((iO) ValueAdjustments::Z).f()).append(' ')
- .append(((iO) ValueAdjustments::b).f()).append(' ')
- .append(((iO) ValueAdjustments::B).f()).append(' ')
- .append(((iO) ValueAdjustments::c).f()).append(' ')
- .append(((iO) ValueAdjustments::C).f()).append(' ')
- .append(((iO) ValueAdjustments::s).f()).append(' ')
- .append(((iO) ValueAdjustments::S).f()).append(' ')
- .append(((iO) ValueAdjustments::i).f()).append(' ')
- .append(((iO) ValueAdjustments::I).f()).append(' ')
- .append(((iO) ValueAdjustments::j).f()).append(' ')
- .append(((iO) ValueAdjustments::J).f()).append(' ')
- .append(((iO) ValueAdjustments::f).f()).append(' ')
- .append(((iO) ValueAdjustments::F).f()).append(' ')
- .append(((iO) ValueAdjustments::d).f()).append(' ')
- .append(((iO) ValueAdjustments::D).f()).append('\n');
+ .append(((iObject) ValueAdjustments::z).f()).append(' ')
+ .append(((iObject) ValueAdjustments::Z).f()).append(' ')
+ .append(((iObject) ValueAdjustments::b).f()).append(' ')
+ .append(((iObject) ValueAdjustments::B).f()).append(' ')
+ .append(((iObject) ValueAdjustments::c).f()).append(' ')
+ .append(((iObject) ValueAdjustments::C).f()).append(' ')
+ .append(((iObject) ValueAdjustments::s).f()).append(' ')
+ .append(((iObject) ValueAdjustments::S).f()).append(' ')
+ .append(((iObject) ValueAdjustments::i).f()).append(' ')
+ .append(((iObject) ValueAdjustments::I).f()).append(' ')
+ .append(((iObject) ValueAdjustments::j).f()).append(' ')
+ .append(((iObject) ValueAdjustments::J).f()).append(' ')
+ .append(((iObject) ValueAdjustments::f).f()).append(' ')
+ .append(((iObject) ValueAdjustments::F).f()).append(' ')
+ .append(((iObject) ValueAdjustments::d).f()).append(' ')
+ .append(((iObject) ValueAdjustments::D).f()).append('\n');
}
private static void checkNumber(StringBuffer builder) {
builder
- .append(((iN) ValueAdjustments::b).f()).append(' ')
- .append(((iN) ValueAdjustments::B).f()).append(' ')
- .append(((iN) ValueAdjustments::s).f()).append(' ')
- .append(((iN) ValueAdjustments::S).f()).append(' ')
- .append(((iN) ValueAdjustments::i).f()).append(' ')
- .append(((iN) ValueAdjustments::I).f()).append(' ')
- .append(((iN) ValueAdjustments::j).f()).append(' ')
- .append(((iN) ValueAdjustments::J).f()).append(' ')
- .append(((iN) ValueAdjustments::f).f()).append(' ')
- .append(((iN) ValueAdjustments::F).f()).append(' ')
- .append(((iN) ValueAdjustments::d).f()).append(' ')
- .append(((iN) ValueAdjustments::D).f()).append('\n');
+ .append(((iNumber) ValueAdjustments::b).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::B).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::s).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::S).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::i).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::I).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::j).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::J).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::f).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::F).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::d).f()).append(' ')
+ .append(((iNumber) ValueAdjustments::D).f()).append('\n');
}
private static void checkBoxes(StringBuffer builder) {
builder
- .append(((iZ) ValueAdjustments::z).f()).append(' ')
- .append(((iB) ValueAdjustments::b).f()).append(' ')
- .append(((iC) ValueAdjustments::c).f()).append(' ')
- .append(((iS) ValueAdjustments::s).f()).append(' ')
- .append(((iI) ValueAdjustments::i).f()).append(' ')
- .append(((iJ) ValueAdjustments::j).f()).append(' ')
- .append(((iF) ValueAdjustments::f).f()).append(' ')
- .append(((iD) ValueAdjustments::d).f()).append('\n');
+ .append(((iBoolean) ValueAdjustments::z).f()).append(' ')
+ .append(((iByte) ValueAdjustments::b).f()).append(' ')
+ .append(((iCharacter) ValueAdjustments::c).f()).append(' ')
+ .append(((iShort) ValueAdjustments::s).f()).append(' ')
+ .append(((iInteger) ValueAdjustments::i).f()).append(' ')
+ .append(((iLong) ValueAdjustments::j).f()).append(' ')
+ .append(((iFloat) ValueAdjustments::f).f()).append(' ')
+ .append(((iDouble) ValueAdjustments::d).f()).append('\n');
}
private static void checkDouble(StringBuffer builder) {
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 803d760..2159d6b 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 @@
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 @@
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 @@
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 d707614..c2ac969 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 @@
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 5b2977c..99fe81c 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 @@
@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 6f51624..7a56e3c 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,462 @@
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.
+ if (ToolHelper.artSupported()) {
+ 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.
+ if (ToolHelper.artSupported()) {
+ 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 8ffab3c..7cb47e2 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 @@
.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 @@
// 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 @@
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 @@
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 6c45f40..e63b155 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 @@
.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 @@
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 7594b0c..9d745f6 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 @@
thrown.expect(ApiLevelException.class);
test("staticinterfacemethods-error-due-to-min-sdk", "interfacemethods",
"StaticInterfaceMethods")
- .run();
+ .withInterfaceMethodDesugaring(OffOrAuto.Off)
+ .run();
}
@Test
@@ -149,6 +150,7 @@
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 0ca84a1..7a0e793 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.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 @@
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 @@
});
}
- 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 @@
}
}
+ 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 @@
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 @@
}
}
- 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 @@
// 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 @@
// 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,12 @@
// Dex version not supported
"invokepolymorphic",
"invokecustom",
- "invokecustom2"
+ "invokecustom2",
+ "DefaultMethodInAndroidJar25",
+ "StaticMethodInAndroidJar25",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
),
DexVm.ART_7_0_0, ImmutableList.of(
// API not supported
@@ -190,7 +215,10 @@
// Dex version not supported
"invokepolymorphic",
"invokecustom",
- "invokecustom2"
+ "invokecustom2",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
),
DexVm.ART_DEFAULT, ImmutableList.of(
)
@@ -266,6 +294,24 @@
}
@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 +361,34 @@
.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 4f84303..3767286 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.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 fa78fd0..c89e792 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 @@
.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/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 12fd23d..35aeab2 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -9,6 +9,7 @@
import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Tag;
import org.apache.harmony.jpda.tests.framework.jdwp.Value;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
/**
@@ -19,6 +20,50 @@
public static final String SOURCE_FILE = "Locals.java";
@Test
+ // TODO(b/65402086) Remove @ignore when debug behavior will be fixed.
+ @Ignore
+ public void testLocalConstantBis() throws Throwable {
+ final String className = "Locals";
+ final String methodName = "localConstantBis";
+ runDebugTest(className,
+ breakpoint(className, methodName),
+ run(),
+ checkLine(SOURCE_FILE, 332),
+ checkNoLocal("result"),
+ stepOver(),
+ checkLine(SOURCE_FILE, 333),
+ checkLocal("result", Value.createInt(0)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 334),
+ checkLocal("result", Value.createInt(0)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 338),
+ checkLocal("result", Value.createInt(1)),
+ run());
+ }
+
+ @Test
+ public void testLocalConstant() throws Throwable {
+ final String className = "Locals";
+ final String methodName = "localConstant";
+ runDebugTest(className,
+ breakpoint(className, methodName),
+ run(),
+ checkLine(SOURCE_FILE, 322),
+ checkNoLocal("result1"),
+ checkNoLocal("result2"),
+ stepOver(),
+ checkLine(SOURCE_FILE, 323),
+ checkNoLocal("result1"),
+ checkNoLocal("result2"),
+ stepOver(),
+ checkLine(SOURCE_FILE, 324),
+ checkLocal("result1"),
+ checkNoLocal("result2"),
+ run());
+ }
+
+ @Test
public void testNoLocal() throws Throwable {
final String className = "Locals";
final String methodName = "noLocals";
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 b6debff..39d90d6 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 @@
}
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 @@
.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);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
index 531831b..835474b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import org.junit.Ignore;
import org.junit.Test;
public class DebugLocalTests extends JasminTestBase {
@@ -116,6 +117,7 @@
}
@Test
+ @Ignore("b/65430598")
public void testNoLocalInfoOnStack() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");