diff options
author | Yohann Roussel <yroussel@google.com> | 2017-09-04 21:58:17 +0000 |
---|---|---|
committer | Yohann Roussel <yroussel@google.com> | 2017-09-04 21:58:17 +0000 |
commit | 4f3a8b35a1fa10972497d3a071be00d2237ce42c (patch) | |
tree | 5aa79d000b667950499ba3d2c538b3615fd9400c | |
parent | 9d00aca8b5a5e6e4a589afeb683de26c58048a1c (diff) | |
download | r8-4f3a8b35a1fa10972497d3a071be00d2237ce42c.tar.gz |
Revert "Loosen desugaring constraints"
This reverts commit 9d00aca8b5a5e6e4a589afeb683de26c58048a1c.
Change-Id: I2b3f24c8291e24400ddfd99ba7f7fd2cd11a30fa
26 files changed, 140 insertions, 989 deletions
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index c8606eec6..3618ab5a1 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java @@ -217,6 +217,8 @@ public class D8Command extends BaseCommand { internal.skipMinification = true; assert internal.useTreeShaking; internal.useTreeShaking = false; + assert internal.interfaceMethodDesugaring == OffOrAuto.Off; + assert internal.tryWithResourcesDesugaring == OffOrAuto.Off; assert internal.inlineAccessors; internal.inlineAccessors = false; assert internal.removeSwitchMaps; diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java index c40e2d590..b7036f521 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,7 +15,6 @@ import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode; import com.android.tools.r8.ir.synthetic.SynthesizedCode; import com.google.common.collect.Sets; import java.util.ArrayList; -import java.util.Collections; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; @@ -43,7 +42,9 @@ final class ClassProcessor { } final void process(DexClass clazz) { - assert !clazz.isInterface(); + if (clazz.isInterface()) { + throw new CompilationError("Interface in superclass chain."); + } 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. @@ -60,15 +61,8 @@ final class ClassProcessor { // about methods added to superclasses when we decide if we want to add a default // method to class `clazz`. DexType superType = clazz.superType; - // If 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 (superType != null && superType != rewriter.factory.objectType) { + process(rewriter.findRequiredClass(superType)); } if (clazz.interfaces.isEmpty()) { @@ -101,10 +95,7 @@ final class ClassProcessor { private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) { DexMethod method = defaultMethod.method; - // 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(); + assert !rewriter.findRequiredClass(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); @@ -122,59 +113,23 @@ final class ClassProcessor { // in this class. private List<DexEncodedMethod> collectMethodsToImplement(DexClass clazz) { DefaultMethodsHelper helper = new DefaultMethodsHelper(); - DexClass current = clazz; + // Collect candidate default methods by inspecting interfaces implemented // by this class as well as its superclasses. - // - // 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) { + DexClass current = clazz; + while (true) { for (DexType type : current.interfaces.values) { - helper.merge(getOrCreateInterfaceInfo(clazz, current, type)); - } - - List<DexEncodedMethod> defaultMethodsInDirectInterface = helper.createFullList(); - List<DexEncodedMethod> toBeImplementedFromDirectInterface = - new ArrayList<>(defaultMethodsInDirectInterface.size()); - hideCandidates(clazz.virtualMethods(), - 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(); + helper.merge(getOrCreateInterfaceInfo(type)); } - if (current.superType == null) { + 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; - } else { - DexClass superClass = rewriter.findDefinitionFor(current.superType); - if (superClass != null) { - current = superClass; - } else { - String message = "Default method desugaring of `" + clazz.toSourceString() + "` failed"; - if (current == clazz) { - message += " because its super class `" + clazz.superType.toSourceString() - + "` is missing"; - } else { - message += - " because it's hierarchy is incomplete. The class `" - + current.superType.toSourceString() - + "` is missing and it is the declared super class of `" - + current.toSourceString() + "`"; - } - throw new CompilationError(message); - } } + current = rewriter.findRequiredClass(superType); } List<DexEncodedMethod> candidates = helper.createCandidatesList(); @@ -193,31 +148,15 @@ final class ClassProcessor { } DexType superType = current.superType; - 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) { + if (superType == 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 = superClass; + current = rewriter.findRequiredClass(superType); } } @@ -249,56 +188,42 @@ final class ClassProcessor { } } - private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo( - DexClass classToDesugar, - DexClass implementing, - DexType iface) { + private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(DexType iface) { DefaultMethodsHelper.Collection collection = cache.get(iface); if (collection != null) { return collection; } - collection = createInterfaceInfo(classToDesugar, implementing, iface); + collection = createInterfaceInfo(iface); cache.put(iface, collection); return collection; } - private DefaultMethodsHelper.Collection createInterfaceInfo( - DexClass classToDesugar, - DexClass implementing, - DexType iface) { + private DefaultMethodsHelper.Collection createInterfaceInfo(DexType iface) { DefaultMethodsHelper helper = new DefaultMethodsHelper(); - 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 (!definedInterface.isInterface()) { + DexClass clazz = rewriter.findRequiredClass(iface); + if (!clazz.isInterface()) { throw new CompilationError( - "Type " + iface.toSourceString() + " is referenced as an interface of `" - + implementing.toString() + "`."); + "Type " + iface.toSourceString() + " is expected to be an interface."); + } + 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; } // Merge information from all superinterfaces. - for (DexType superinterface : definedInterface.interfaces.values) { - helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface)); + for (DexType superinterface : clazz.interfaces.values) { + helper.merge(getOrCreateInterfaceInfo(superinterface)); } // Hide by virtual methods of this interface. - for (DexEncodedMethod virtual : definedInterface.virtualMethods()) { + for (DexEncodedMethod virtual : clazz.virtualMethods()) { helper.hideMatches(virtual.method); } // Add all default methods of this interface. - for (DexEncodedMethod encoded : definedInterface.virtualMethods()) { + for (DexEncodedMethod encoded : clazz.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 ae9a2ae9f..2f25b1972 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,10 +6,8 @@ package com.android.tools.r8.ir.desugar; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; @@ -104,18 +102,6 @@ final class DefaultMethodsHelper { return candidates; } - final List<DexEncodedMethod> createFullList() { - if (candidates.isEmpty() && hidden.isEmpty()) { - return Collections.emptyList(); - } - - List<DexEncodedMethod> fullList = - new ArrayList<DexEncodedMethod>(candidates.size() + hidden.size()); - fullList.addAll(candidates); - fullList.addAll(hidden); - return fullList; - } - // Create default interface collection based on collected information. final Collection wrapInCollection() { candidates.removeAll(hidden); diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java index 5c8faee77..c3f2d087f 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 @@ -11,7 +11,6 @@ import com.android.tools.r8.graph.DexApplication.Builder; import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.graph.DexItem; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexMethodHandle; @@ -25,7 +24,6 @@ import com.android.tools.r8.ir.code.InstructionListIterator; import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.code.InvokeSuper; import com.android.tools.r8.ir.conversion.IRConverter; -import com.android.tools.r8.logging.Log; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.util.ListIterator; @@ -69,11 +67,6 @@ public final class InterfaceMethodRewriter { // to this collection since it is only filled in ClassProcessor running synchronously. private final Set<DexEncodedMethod> forwardingMethods = Sets.newIdentityHashSet(); - /** - * A set of dexitems we have reported missing to dedupe warnings. - */ - private Set<DexItem> reportedMissing = Sets.newIdentityHashSet(); - /** Defines a minor variation in desugaring. */ public enum Flavor { /** Process all application resources. */ @@ -106,11 +99,10 @@ public final class InterfaceMethodRewriter { // Check that static interface methods are not referenced // from invoke-custom instructions via method handles. DexCallSite callSite = instruction.asInvokeCustom().getCallSite(); - reportStaticInterfaceMethodHandle(encodedMethod.method, callSite.bootstrapMethod); + reportStaticInterfaceMethodHandle(callSite.bootstrapMethod); for (DexValue arg : callSite.bootstrapArgs) { if (arg instanceof DexValue.DexValueMethodHandle) { - reportStaticInterfaceMethodHandle(encodedMethod.method, - ((DexValue.DexValueMethodHandle) arg).value); + reportStaticInterfaceMethodHandle(((DexValue.DexValueMethodHandle) arg).value); } } continue; @@ -119,17 +111,7 @@ public final class InterfaceMethodRewriter { if (instruction.isInvokeStatic()) { InvokeStatic invokeStatic = instruction.asInvokeStatic(); DexMethod method = invokeStatic.getInvokedMethod(); - 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! + if (isInterfaceClass(method.holder)) { // Retarget call to an appropriate method of companion class. instructions.replaceCurrentInstruction( new InvokeStatic(staticAsMethodOfCompanionClass(method), @@ -141,17 +123,7 @@ public final class InterfaceMethodRewriter { if (instruction.isInvokeSuper()) { InvokeSuper invokeSuper = instruction.asInvokeSuper(); DexMethod method = invokeSuper.getInvokedMethod(); - 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! + if (isInterfaceClass(method.holder)) { // Retarget call to an appropriate method of companion class. instructions.replaceCurrentInstruction( new InvokeStatic(defaultAsMethodOfCompanionClass(method), @@ -162,27 +134,25 @@ public final class InterfaceMethodRewriter { } } - 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 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."); } } - /** - * 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); + 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."); } // Gets the companion class for the interface `type`. @@ -291,18 +261,4 @@ public final class InterfaceMethodRewriter { } return true; } - - void warnMissingClass(DexType missing, String message) { - // TODO replace by a proper warning mechanic (see b/65154707). - // TODO think about using a common deduplicating mechanic with Enqueuer - if (reportedMissing.add(missing)) { - System.err.println(message); - } - } - - private void warnMissingClass(DexItem referencedFrom, DexType clazz) { - warnMissingClass(clazz, - "Type `" + clazz.toSourceString() + "` was not found, it is required for default or" - + " static interface methods desugaring of `" + referencedFrom.toSourceString() + "`"); - } } diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index 1b9272ce4..b03721fa5 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java @@ -83,9 +83,9 @@ public class InternalOptions { public List<String> logArgumentsFilter = ImmutableList.of(); // Defines interface method rewriter behavior. - public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Auto; + public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Off; // Defines try-with-resources rewriter behavior. - public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Auto; + public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Off; // Application writing mode. public OutputMode outputMode = OutputMode.Indexed; diff --git a/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java b/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java deleted file mode 100644 index bec4e6731..000000000 --- a/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java +++ /dev/null @@ -1,100 +0,0 @@ -// 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 deleted file mode 100644 index f07ea8faf..000000000 --- a/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java +++ /dev/null @@ -1,17 +0,0 @@ -// 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 deleted file mode 100644 index 98504bd45..000000000 --- a/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java +++ /dev/null @@ -1,10 +0,0 @@ -// 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/desugaringwithmissingclasslib2/B.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java deleted file mode 100644 index 1ab424e77..000000000 --- a/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java +++ /dev/null @@ -1,13 +0,0 @@ -// 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 deleted file mode 100644 index 029f9f4cd..000000000 --- a/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java +++ /dev/null @@ -1,9 +0,0 @@ -// 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/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java deleted file mode 100644 index 5f3c1c2a0..000000000 --- a/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java +++ /dev/null @@ -1,10 +0,0 @@ -// 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 deleted file mode 100644 index 411a0162e..000000000 --- a/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java +++ /dev/null @@ -1,21 +0,0 @@ -// 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 deleted file mode 100644 index f2ef1b623..000000000 --- a/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java +++ /dev/null @@ -1,10 +0,0 @@ -// 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 deleted file mode 100644 index c42ac8127..000000000 --- a/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java +++ /dev/null @@ -1,21 +0,0 @@ -// 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 deleted file mode 100644 index 482623705..000000000 --- a/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java +++ /dev/null @@ -1,18 +0,0 @@ -// 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 deleted file mode 100644 index bde4d09e9..000000000 --- a/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java +++ /dev/null @@ -1,32 +0,0 @@ -// 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/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java index 2159d6b83..803d76022 100644 --- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java @@ -38,14 +38,14 @@ import org.junit.Test; public abstract class D8IncrementalRunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command.Builder> { - abstract class D8IncrementalTestRunner extends TestRunner<D8IncrementalTestRunner> { + abstract class D8IncrementalTestRunner extends TestRunner { D8IncrementalTestRunner(String testName, String packageName, String mainClass) { super(testName, packageName, mainClass); } @Override - D8IncrementalTestRunner withMinApiLevel(int minApiLevel) { + TestRunner withMinApiLevel(int minApiLevel) { return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel)); } @@ -154,11 +154,12 @@ public abstract class D8IncrementalRunExamplesAndroidOTest builder = transformation.apply(builder); } builder = builder.setOutputMode(outputMode); + builder = builder.addLibraryFiles( + Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel()))); if (output != null) { builder = builder.setOutputPath(output); } - addLibraryReference(builder, Paths.get(ToolHelper.getAndroidJar( - androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion))); + addLibraryReference(builder, Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel()))); D8Command command = builder.build(); try { return ToolHelper.runD8(command, this::combinedOptionConsumer); @@ -287,7 +288,6 @@ public abstract class D8IncrementalRunExamplesAndroidOTest Assert.assertArrayEquals(expectedFileNames, dexFiles); } - @Override abstract D8IncrementalTestRunner test(String testName, String packageName, String mainClass); static byte[] readFromResource(Resource resource) throws IOException { diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java index c2ac969a7..d7076148d 100644 --- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java @@ -52,11 +52,6 @@ public class D8LazyRunExamplesAndroidOTest builder.addLibraryResourceProvider( PreloadedClassFileProvider.fromArchive(location)); } - - @Override - D8LazyTestRunner self() { - return this; - } } @Override diff --git a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java index 99fe81cfe..5b2977c47 100644 --- a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java @@ -23,13 +23,7 @@ public class D8NonLazyRunExamplesAndroidOTest @Override void addLibraryReference(D8Command.Builder builder, Path location) throws IOException { - builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar( - androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion))); - } - - @Override - D8LazyTestRunner self() { - return this; + builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel()))); } } diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java index b2adb201a..6f516242d 100644 --- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java @@ -4,58 +4,33 @@ 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<D8TestRunner> { + class D8TestRunner extends TestRunner { D8TestRunner(String testName, String packageName, String mainClass) { super(testName, packageName, mainClass); } @Override - D8TestRunner withMinApiLevel(int minApiLevel) { + TestRunner 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( - androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion))); + builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel()))); D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build(); try { ToolHelper.runD8(command, this::combinedOptionConsumer); @@ -65,361 +40,10 @@ public class D8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command throw re.getCause() == null ? re : re.getCause(); } } - - @Override - D8TestRunner self() { - return this; - } - } - - @Test - public void testDefaultInInterfaceWithoutDesugaring() throws Throwable { - // lib1: interface A { default String foo() { return "A"; } } - D8TestRunner lib1 = - test("testDefaultInInterfaceWithoutDesugaring", "desugaringwithmissingclasslib1", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Off) - .withMinApiLevel(ANDROID_K_API); - try { - lib1.build(); - - // compilation should have failed on CompilationError since A is declaring a default method. - Assert.fail(); - } catch (CompilationError | CompilationException e) { - // Expected. - } - } - - @Test - public void testMissingInterfaceDesugared() throws Throwable { - // lib1: interface A { default String foo() { return "A"; } } - D8TestRunner lib1 = - test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withMinApiLevel(ANDROID_K_API); - lib1.build(); - - // lib2: interface B extends A { default String foo() { return "B"; } } - // lib2 is compiled with full classpath - D8TestRunner lib2 = - test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(ANDROID_K_API); - lib2.build(); - - // test: class ImplementMethodsWithDefault implements A, B {} should get its foo implementation - // from B. - // test is compiled with incomplete classpath: lib2 is missing so ImplementMethodsWithDefault is - // missing one of it interfaces. - D8TestRunner test = - test("desugaringwithmissingclasstest1", "desugaringwithmissingclasstest1", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(ANDROID_K_API); - test.build(); - - // TODO check compilation warnings are correctly reported - // B is missing so compiled code makes no sense, no need to test execution. - } - - @Test - public void testMissingInterfaceDesugared2AndroidK() throws Throwable { - int minApi = ANDROID_K_API; - - // lib1: interface A { default String foo() { return "A"; } } - D8TestRunner lib1 = - test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withMinApiLevel(minApi); - Path lib1Dex = lib1.build(); - - // lib2: interface B extends A { default String foo() { return "B"; } } - // lib2 is compiled with full classpath - D8TestRunner lib2 = - test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib2Dex = lib2.build(); - - // lib3: class C implements A {} - // lib3 is compiled with full classpath - D8TestRunner lib3 = - test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib3Dex = lib3.build(); - - // test: class ImplementMethodsWithDefault extends C implements B should get its foo - // implementation from B. - // test is compiled with incomplete classpath: lib2 and lib3 are missing so - // ImplementMethodsWithDefault is missing all its hierarchy. - D8TestRunner test = - test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path testDex = test.build(); - // TODO check compilation warnings are correctly reported - - // Missing interface B is causing the wrong code to be executed. - thrown.expect(AssertionError.class); - execute( - "testMissingInterfaceDesugared2AndroidK", - "desugaringwithmissingclasstest2.Main", - new Path[] { - lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() - }, - new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); - } - - @Test - public void testMissingInterfaceDesugared2AndroidO() throws Throwable { - int minApi = ANDROID_O_API; - // lib1: interface A { default String foo() { return "A"; } } - D8TestRunner lib1 = - test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withMinApiLevel(minApi); - Path lib1Dex = lib1.build(); - - // lib2: interface B extends A { default String foo() { return "B"; } } - // lib2 is compiled with full classpath - D8TestRunner lib2 = - test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib2Dex = lib2.build(); - - // lib3: class C implements A {} - // lib3 is compiled with full classpath - D8TestRunner lib3 = - test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib3Dex = lib3.build(); - - // test: class ImplementMethodsWithDefault extends C implements B should get its foo - // implementation from B. - // test is compiled with incomplete classpath: lib2 and lib3 are missing so - // ImplementMethodsWithDefault is missing all its hierarchy. - D8TestRunner test = - test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path testDex = test.build(); - execute( - "testMissingInterfaceDesugared2AndroidO", - "desugaringwithmissingclasstest2.Main", - new Path[] { - lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() - }, - new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); - } - - @Test - public void testCallToMissingSuperInterfaceDesugaredAndroidK() throws Throwable { - - int minApi = ANDROID_K_API; - // lib1: interface A { default String foo() { return "A"; } } - D8TestRunner lib1 = - test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withMinApiLevel(minApi); - Path lib1Dex = lib1.build(); - - // lib2: interface B extends A { default String foo() { return "B"; } } - // lib2 is compiled with full classpath - D8TestRunner lib2 = - test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib2Dex = lib2.build(); - - // lib3: class C implements A {} - // lib3 is compiled with full classpath - D8TestRunner lib3 = - test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib3Dex = lib3.build(); - - // test: class ImplementMethodsWithDefault extends C implements B - // { String getB() { return B.super.foo(); } - // Should be able to call implementation from B. - // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing. - D8TestRunner test = - test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar(), lib3.getInputJar()) - .withMinApiLevel(minApi); - Path testDex = test.build(); - // TODO check compilation warnings are correctly reported - - // Missing interface B is causing the wrong method to be executed. - thrown.expect(AssertionError.class); - execute( - "testCallToMissingSuperInterfaceDesugaredAndroidK", - "desugaringwithmissingclasstest3.Main", - new Path[] { - lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() - }, - new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); - } - - @Test - public void testCallToMissingSuperInterfaceDesugaredAndroidO() throws Throwable { - int minApi = ANDROID_O_API; - // lib1: interface A { default String foo() { return "A"; } } - D8TestRunner lib1 = - test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withMinApiLevel(minApi); - Path lib1Dex = lib1.build(); - - // lib2: interface B extends A { default String foo() { return "B"; } } - // lib2 is compiled with full classpath - D8TestRunner lib2 = - test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib2Dex = lib2.build(); - - // lib3: class C implements A {} - // lib3 is compiled with full classpath - D8TestRunner lib3 = - test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib3Dex = lib3.build(); - - // test: class ImplementMethodsWithDefault extends C implements B - // { String getB() { return B.super.foo(); } - // Should be able to call implementation from B. - // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing. - D8TestRunner test = - test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar(), lib3.getInputJar()) - .withMinApiLevel(minApi); - Path testDex = test.build(); - execute( - "testCallToMissingSuperInterfaceDesugaredAndroidO", - "desugaringwithmissingclasstest3.Main", - new Path[] { - lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() - }, - new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); - } - - @Test - public void testMissingSuperDesugaredAndroidK() throws Throwable { - int minApi = ANDROID_K_API; - - // lib1: interface A { default String foo() { return "A"; } } - D8TestRunner lib1 = - test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withMinApiLevel(minApi); - lib1.build(); - - // lib2: interface B extends A { default String foo() { return "B"; } } - // lib2 is compiled with full classpath - D8TestRunner lib2 = - test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - lib2.build(); - - // lib3: class C implements A {} - // lib3 is compiled with full classpath - D8TestRunner lib3 = - test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - lib3.build(); - - // test: class ImplementMethodsWithDefault extends C implements B should get its foo - // implementation from B. - // test is compiled with incomplete classpath: lib3 is missing so - // ImplementMethodsWithDefault is missing its super class. - D8TestRunner test = - test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withClasspath(lib2.getInputJar()) - .withMinApiLevel(minApi); - thrown.expect( - new CombinableMatcher<CompilationError>(new IsInstanceOf(CompilationError.class)) - .and(new ThrowableMessageMatcher<CompilationError>( - new StringContains("desugaringwithmissingclasstest2.ImplementMethodsWithDefault"))) - .and(new ThrowableMessageMatcher<CompilationError>( - new StringContains("desugaringwithmissingclasslib3.C")))); - test.build(); - } - - @Test - public void testMissingSuperDesugaredAndroidO() throws Throwable { - int minApi = ANDROID_O_API; - - // lib1: interface A { default String foo() { return "A"; } } - D8TestRunner lib1 = - test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withMinApiLevel(minApi); - Path lib1Dex = lib1.build(); - - // lib2: interface B extends A { default String foo() { return "B"; } } - // lib2 is compiled with full classpath - D8TestRunner lib2 = - test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib2Dex = lib2.build(); - - // lib3: class C implements A {} - // lib3 is compiled with full classpath - D8TestRunner lib3 = - test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withMinApiLevel(minApi); - Path lib3Dex = lib3.build(); - - // test: class ImplementMethodsWithDefault extends C implements B should get its foo - // implementation from B. - // test is compiled with incomplete classpath: lib3 is missing so - // ImplementMethodsWithDefault is missing its super class. - D8TestRunner test = - test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A") - .withInterfaceMethodDesugaring(OffOrAuto.Auto) - .withClasspath(lib1.getInputJar()) - .withClasspath(lib2.getInputJar()) - .withMinApiLevel(minApi); - Path testDex = test.build(); - - execute( - "testMissingSuperDesugaredAndroidO", - "desugaringwithmissingclasstest2.Main", - new Path[] { - lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() - }, - new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); } @Override - D8TestRunner test(String testName, String packageName, String mainClass) { + TestRunner 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 20b8b9c18..aa3061598 100644 --- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java +++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java @@ -136,6 +136,21 @@ public abstract class R8RunArtTestsTest { .put("975-iface-private", Constants.ANDROID_N_API) .build(); + // Tests requiring interface method desugaring because they explicitly or + // implicitly define static or default interface methods and are enabled + // on pre-N Android. + private static List<String> enableInterfaceMethodDesugaring = + ImmutableList.of( + "563-checker-invoke-super", + "604-hot-static-interface", + "961-default-iface-resolution-gen", + "963-default-range-smali", + "965-default-verify", + "967-default-ame", + "969-iface-super", + "978-virtual-interface" + ); + // Tests that timeout when run with Art. private static final Multimap<String, TestCondition> timeoutOrSkipRunWithArt = new ImmutableListMultimap.Builder<String, TestCondition>() @@ -184,9 +199,8 @@ public abstract class R8RunArtTestsTest { // Tests that are never compiled or run. private static List<String> skipAltogether = ImmutableList.of( - // Those tests contains an invalid type hierarchy, which we cannot currently handle. + // This test 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", @@ -1125,12 +1139,7 @@ public abstract class R8RunArtTestsTest { Integer minSdkVersion = needMinSdkVersion.get(name); if (minSdkVersion != null) { builder.setMinApiLevel(minSdkVersion); - builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdkVersion))); - } else { - builder.addLibraryFiles(Paths.get( - ToolHelper.getAndroidJar(Constants.DEFAULT_ANDROID_API))); } - D8Output output = D8.run(builder.build()); output.write(Paths.get(resultPath)); break; @@ -1153,6 +1162,9 @@ public abstract class R8RunArtTestsTest { ToolHelper.runR8( builder.build(), options -> { + if (enableInterfaceMethodDesugaring.contains(name)) { + options.interfaceMethodDesugaring = OffOrAuto.Auto; + } if (disableInlining) { options.inlineAccessors = false; } diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java index e63b1553b..6c45f40a4 100644 --- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java @@ -46,14 +46,14 @@ public class R8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<R8Command .run(); } - class R8TestRunner extends TestRunner<R8TestRunner> { + class R8TestRunner extends TestRunner { R8TestRunner(String testName, String packageName, String mainClass) { super(testName, packageName, mainClass); } @Override - R8TestRunner withMinApiLevel(int minApiLevel) { + TestRunner withMinApiLevel(int minApiLevel) { return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel)); } @@ -64,23 +64,16 @@ public class R8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<R8Command for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) { builder = transformation.apply(builder); } - builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar( - androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion))); R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build(); ToolHelper.runR8(command, this::combinedOptionConsumer); } catch (ExecutionException e) { throw e.getCause(); } } - - @Override - R8TestRunner self() { - return this; - } } @Override - R8TestRunner test(String testName, String packageName, String mainClass) { + TestRunner 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 9d745f623..7594b0cf2 100644 --- a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java +++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java @@ -133,8 +133,7 @@ public abstract class RunExamplesAndroidNTest<B> { thrown.expect(ApiLevelException.class); test("staticinterfacemethods-error-due-to-min-sdk", "interfacemethods", "StaticInterfaceMethods") - .withInterfaceMethodDesugaring(OffOrAuto.Off) - .run(); + .run(); } @Test @@ -150,7 +149,6 @@ public abstract class RunExamplesAndroidNTest<B> { thrown.expect(ApiLevelException.class); test("defaultmethods-error-due-to-min-sdk", "interfacemethods", "DefaultMethods") - .withInterfaceMethodDesugaring(OffOrAuto.Off) .run(); } diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java index 012a077a7..937f55c63 100644 --- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java @@ -22,35 +22,29 @@ import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.OffOrAuto; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; -import java.util.stream.Collectors; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -public abstract class RunExamplesAndroidOTest - <B extends BaseCommand.Builder<? extends BaseCommand, B>> { +public abstract class RunExamplesAndroidOTest<B> { static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR; - abstract class TestRunner<C extends TestRunner<C>> { + abstract class TestRunner { 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<>(); @@ -61,22 +55,20 @@ public abstract class RunExamplesAndroidOTest this.mainClass = mainClass; } - abstract C self(); - - C withDexCheck(Consumer<DexInspector> check) { + TestRunner withDexCheck(Consumer<DexInspector> check) { dexInspectorChecks.add(check); - return self(); + return this; } - C withClassCheck(Consumer<FoundClassSubject> check) { + TestRunner withClassCheck(Consumer<FoundClassSubject> check) { return withDexCheck(inspector -> inspector.forAllClasses(check)); } - C withMethodCheck(Consumer<FoundMethodSubject> check) { + TestRunner withMethodCheck(Consumer<FoundMethodSubject> check) { return withClassCheck(clazz -> clazz.forAllMethods(check)); } - <T extends InstructionSubject> C withInstructionCheck( + <T extends InstructionSubject> TestRunner withInstructionCheck( Predicate<InstructionSubject> filter, Consumer<T> check) { return withMethodCheck(method -> { if (method.isAbstract()) { @@ -89,22 +81,22 @@ public abstract class RunExamplesAndroidOTest }); } - C withOptionConsumer(Consumer<InternalOptions> consumer) { + TestRunner withOptionConsumer(Consumer<InternalOptions> consumer) { optionConsumers.add(consumer); - return self(); + return this; } - C withInterfaceMethodDesugaring(OffOrAuto behavior) { + TestRunner withInterfaceMethodDesugaring(OffOrAuto behavior) { return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior); } - C withTryWithResourcesDesugaring(OffOrAuto behavior) { + TestRunner withTryWithResourcesDesugaring(OffOrAuto behavior) { return withOptionConsumer(o -> o.tryWithResourcesDesugaring = behavior); } - C withBuilderTransformation(UnaryOperator<B> builderTransformation) { + TestRunner withBuilderTransformation(UnaryOperator<B> builderTransformation) { builderTransformations.add(builderTransformation); - return self(); + return this; } void combinedOptionConsumer(InternalOptions options) { @@ -113,18 +105,6 @@ public abstract class RunExamplesAndroidOTest } } - 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 (compilationErrorExpected(testName)) { thrown.expect(CompilationError.class); @@ -134,7 +114,7 @@ public abstract class RunExamplesAndroidOTest } String qualifiedMainClass = packageName + "." + mainClass; - Path inputFile = getInputJar(); + Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION); Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION); build(inputFile, out); @@ -143,6 +123,11 @@ public abstract class RunExamplesAndroidOTest return; } + boolean expectedToFail = expectedToFail(testName); + if (expectedToFail) { + thrown.expect(Throwable.class); + } + if (!dexInspectorChecks.isEmpty()) { DexInspector inspector = new DexInspector(out); for (Consumer<DexInspector> check : dexInspectorChecks) { @@ -150,16 +135,21 @@ public abstract class RunExamplesAndroidOTest } } - execute(testName, qualifiedMainClass, new Path[]{inputFile}, new Path[]{out}); + 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)); + } } - abstract C withMinApiLevel(int minApiLevel); - - C withAndroidJar(int androidJarVersion) { - assert this.androidJarVersion == null; - this.androidJarVersion = androidJarVersion; - return self(); - } + abstract TestRunner withMinApiLevel(int minApiLevel); abstract void build(Path inputFile, Path out) throws Throwable; } @@ -179,11 +169,7 @@ public abstract class RunExamplesAndroidOTest // Dex version not supported "invokepolymorphic", "invokecustom", - "invokecustom2", - "DefaultMethodInAndroidJar25", - "StaticMethodInAndroidJar25", - "testMissingInterfaceDesugared2AndroidO", - "testCallToMissingSuperInterfaceDesugaredAndroidO" + "invokecustom2" ), DexVm.ART_5_1_1, ImmutableList.of( // API not supported @@ -192,11 +178,7 @@ public abstract class RunExamplesAndroidOTest // Dex version not supported "invokepolymorphic", "invokecustom", - "invokecustom2", - "DefaultMethodInAndroidJar25", - "StaticMethodInAndroidJar25", - "testMissingInterfaceDesugared2AndroidO", - "testCallToMissingSuperInterfaceDesugaredAndroidO" + "invokecustom2" ), DexVm.ART_6_0_1, ImmutableList.of( // API not supported @@ -205,11 +187,7 @@ public abstract class RunExamplesAndroidOTest // Dex version not supported "invokepolymorphic", "invokecustom", - "invokecustom2", - "DefaultMethodInAndroidJar25", - "StaticMethodInAndroidJar25", - "testMissingInterfaceDesugared2AndroidO", - "testCallToMissingSuperInterfaceDesugaredAndroidO" + "invokecustom2" ), DexVm.ART_7_0_0, ImmutableList.of( // API not supported @@ -217,9 +195,7 @@ public abstract class RunExamplesAndroidOTest // Dex version not supported "invokepolymorphic", "invokecustom", - "invokecustom2", - "testMissingInterfaceDesugared2AndroidO", - "testCallToMissingSuperInterfaceDesugaredAndroidO" + "invokecustom2" ), DexVm.ART_DEFAULT, ImmutableList.of( ) @@ -299,24 +275,6 @@ public abstract class RunExamplesAndroidOTest } @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) @@ -366,34 +324,5 @@ public abstract class RunExamplesAndroidOTest .run(); } - 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)); - } - } - + abstract TestRunner test(String testName, String packageName, String mainClass); } diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java index 72e9e8138..663d6bd68 100644 --- a/src/test/java/com/android/tools/r8/ToolHelper.java +++ b/src/test/java/com/android/tools/r8/ToolHelper.java @@ -14,11 +14,15 @@ import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.shaking.ProguardConfiguration; import com.android.tools.r8.shaking.ProguardConfigurationParser; +import com.android.tools.r8.shaking.ProguardConfigurationRule; import com.android.tools.r8.shaking.ProguardRuleParserException; +import com.android.tools.r8.shaking.RootSetBuilder; +import com.android.tools.r8.shaking.RootSetBuilder.RootSet; import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; diff --git a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java index 39d90d64d..b6debff76 100644 --- a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java +++ b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java @@ -45,6 +45,14 @@ public class BasicTestDependenciesDesugaringTest { } private static Set<String> knownIssues = Sets.newHashSet(new String[]{ + "espresso-core-3.0.0.jar", + "hamcrest-integration-1.3.jar", + "hamcrest-library-1.3.jar", + "junit-4.12.jar", + "support-core-ui-25.4.0.jar", + "support-media-compat-25.4.0.jar", + "support-fragment-25.4.0.jar", + "support-compat-25.4.0.jar" }); @Rule @@ -90,18 +98,4 @@ public class BasicTestDependenciesDesugaringTest { .build(), options -> options.interfaceMethodDesugaring = OffOrAuto.Auto); } - - @Test - public void testCompileDontDesugarDefault() throws IOException, CompilationException { - if (knownIssues.contains(name)) { - thrown.expect(CompilationError.class); - } - ToolHelper.runD8( - D8Command.builder().addClasspathFiles(classpath) - .addProgramFiles(toCompile) - .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(Constants.ANDROID_K_API))) - .setMinApiLevel(Constants.ANDROID_K_API) - .build(), - options -> options.interfaceMethodDesugaring = OffOrAuto.Off); - } } |