Merge commit 'cda44f1ed34560c324a1031f31542d406cced5ae' into dev-release
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 58e68dc..70d4425 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -335,9 +335,7 @@
// Mark dead proto extensions fields as neither being read nor written. This step must
// run prior to the tree pruner.
appView.withGeneratedExtensionRegistryShrinker(
- shrinker -> {
- shrinker.run(Mode.INITIAL_TREE_SHAKING);
- });
+ shrinker -> shrinker.run(Mode.INITIAL_TREE_SHAKING));
TreePruner pruner = new TreePruner(appViewWithLiveness);
application = pruner.run(application);
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index 7a2c325..f327f21 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -43,25 +43,28 @@
public static class DesugaredLibraryCodeToKeep extends CodeToKeep {
private static class KeepStruct {
+
Set<DexField> fields = Sets.newConcurrentHashSet();
Set<DexMethod> methods = Sets.newConcurrentHashSet();
boolean all = false;
}
private final NamingLens namingLens;
- private final Set<DexType> emulatedInterfaces = Sets.newIdentityHashSet();
+ private final Set<DexType> potentialTypesToKeep = Sets.newIdentityHashSet();
private final Map<DexType, KeepStruct> toKeep = new ConcurrentHashMap<>();
private final InternalOptions options;
public DesugaredLibraryCodeToKeep(NamingLens namingLens, InternalOptions options) {
- emulatedInterfaces.addAll(
- options.desugaredLibraryConfiguration.getEmulateLibraryInterface().values());
this.namingLens = namingLens;
this.options = options;
+ potentialTypesToKeep.addAll(
+ options.desugaredLibraryConfiguration.getEmulateLibraryInterface().values());
+ potentialTypesToKeep.addAll(
+ options.desugaredLibraryConfiguration.getCustomConversions().values());
}
private boolean shouldKeep(DexType type) {
- return namingLens.prefixRewrittenType(type) != null || emulatedInterfaces.contains(type);
+ return namingLens.prefixRewrittenType(type) != null || potentialTypesToKeep.contains(type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
index f573b76..328bd61 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
@@ -16,13 +16,19 @@
DexField getField();
+ int getNumberOfWriteContexts();
+
DexEncodedMethod getUniqueReadContext();
void forEachIndirectAccess(Consumer<DexField> consumer);
void forEachIndirectAccessWithContexts(BiConsumer<DexField, Set<DexEncodedMethod>> consumer);
- void forEachReadContext(Consumer<DexMethod> consumer);
+ void forEachReadContext(Consumer<DexEncodedMethod> consumer);
+
+ void forEachWriteContext(Consumer<DexEncodedMethod> consumer);
+
+ boolean hasReflectiveAccess();
boolean isRead();
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
index 1f07f43..5b0ebc2 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
@@ -4,12 +4,17 @@
package com.android.tools.r8.graph;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
/** Provides immutable access to {@link FieldAccessInfoCollectionImpl}. */
public interface FieldAccessInfoCollection<T extends FieldAccessInfo> {
+ void flattenAccessContexts();
+
T get(DexField field);
void forEach(Consumer<T> consumer);
+
+ void removeIf(BiPredicate<DexField, FieldAccessInfoImpl> predicate);
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 3a307cf..47a9c30 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -16,6 +16,11 @@
private Map<DexField, FieldAccessInfoImpl> infos = new IdentityHashMap<>();
@Override
+ public void flattenAccessContexts() {
+ infos.values().forEach(FieldAccessInfoImpl::flattenAccessContexts);
+ }
+
+ @Override
public FieldAccessInfoImpl get(DexField field) {
return infos.get(field);
}
@@ -32,6 +37,7 @@
infos.values().forEach(consumer);
}
+ @Override
public void removeIf(BiPredicate<DexField, FieldAccessInfoImpl> predicate) {
infos.entrySet().removeIf(entry -> predicate.test(entry.getKey(), entry.getValue()));
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index 5924435..99ad00f 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.errors.Unreachable;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -24,6 +25,9 @@
// A direct reference to the definition of the field.
private DexField field;
+ // If this field has a reflective access.
+ private boolean hasReflectiveAccess;
+
// Maps every direct and indirect reference in a read-context to the set of methods in which that
// reference appears.
private Map<DexField, Set<DexEncodedMethod>> readsWithContexts;
@@ -36,6 +40,29 @@
this.field = field;
}
+ void flattenAccessContexts() {
+ flattenAccessContexts(readsWithContexts);
+ flattenAccessContexts(writesWithContexts);
+ }
+
+ private void flattenAccessContexts(Map<DexField, Set<DexEncodedMethod>> accessesWithContexts) {
+ if (accessesWithContexts != null) {
+ Set<DexEncodedMethod> flattenedAccessContexts =
+ accessesWithContexts.computeIfAbsent(field, ignore -> Sets.newIdentityHashSet());
+ accessesWithContexts.forEach(
+ (access, contexts) -> {
+ if (access != field) {
+ flattenedAccessContexts.addAll(contexts);
+ }
+ });
+ accessesWithContexts.clear();
+ if (!flattenedAccessContexts.isEmpty()) {
+ accessesWithContexts.put(field, flattenedAccessContexts);
+ }
+ assert accessesWithContexts.size() <= 1;
+ }
+ }
+
@Override
public FieldAccessInfoImpl asMutable() {
return this;
@@ -47,6 +74,19 @@
}
@Override
+ public int getNumberOfWriteContexts() {
+ if (writesWithContexts != null) {
+ if (writesWithContexts.size() == 1) {
+ return writesWithContexts.values().iterator().next().size();
+ } else {
+ throw new Unreachable(
+ "Should only be querying the number of write contexts after flattening");
+ }
+ }
+ return 0;
+ }
+
+ @Override
public DexEncodedMethod getUniqueReadContext() {
if (readsWithContexts != null && readsWithContexts.size() == 1) {
Set<DexEncodedMethod> contexts = readsWithContexts.values().iterator().next();
@@ -110,22 +150,41 @@
}
@Override
- public void forEachReadContext(Consumer<DexMethod> consumer) {
+ public void forEachReadContext(Consumer<DexEncodedMethod> consumer) {
+ forEachAccessContext(readsWithContexts, consumer);
+ }
+
+ @Override
+ public void forEachWriteContext(Consumer<DexEncodedMethod> consumer) {
+ forEachAccessContext(writesWithContexts, consumer);
+ }
+
+ private void forEachAccessContext(
+ Map<DexField, Set<DexEncodedMethod>> accessesWithContexts,
+ Consumer<DexEncodedMethod> consumer) {
// There can be indirect reads and writes of the same field reference, so we need to keep track
// of the previously-seen indirect accesses to avoid reporting duplicates.
- Set<DexMethod> visited = Sets.newIdentityHashSet();
- if (readsWithContexts != null) {
- for (Set<DexEncodedMethod> encodedReadContexts : readsWithContexts.values()) {
- for (DexEncodedMethod encodedReadContext : encodedReadContexts) {
- DexMethod readContext = encodedReadContext.method;
- if (visited.add(readContext)) {
- consumer.accept(readContext);
+ Set<DexEncodedMethod> visited = Sets.newIdentityHashSet();
+ if (accessesWithContexts != null) {
+ for (Set<DexEncodedMethod> encodedAccessContexts : accessesWithContexts.values()) {
+ for (DexEncodedMethod encodedAccessContext : encodedAccessContexts) {
+ if (visited.add(encodedAccessContext)) {
+ consumer.accept(encodedAccessContext);
}
}
}
}
}
+ @Override
+ public boolean hasReflectiveAccess() {
+ return hasReflectiveAccess;
+ }
+
+ public void setHasReflectiveAccess() {
+ hasReflectiveAccess = true;
+ }
+
/** Returns true if this field is read by the program. */
@Override
public boolean isRead() {
@@ -208,6 +267,9 @@
public FieldAccessInfoImpl rewrittenWithLens(DexDefinitionSupplier definitions, GraphLense lens) {
FieldAccessInfoImpl rewritten = new FieldAccessInfoImpl(lens.lookupField(field));
+ if (hasReflectiveAccess) {
+ rewritten.setHasReflectiveAccess();
+ }
if (readsWithContexts != null) {
rewritten.readsWithContexts = new IdentityHashMap<>();
readsWithContexts.forEach(
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 09a9055..afac334 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -3,12 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.ir.code.ConstInstruction;
-import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.IteratorUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
@@ -23,14 +18,11 @@
import java.util.Deque;
import java.util.HashSet;
import java.util.IdentityHashMap;
-import java.util.LinkedList;
import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.function.Consumer;
import java.util.function.Function;
/**
@@ -73,270 +65,6 @@
}
}
- public static class RewrittenPrototypeDescription {
-
- public static class RemovedArgumentInfo {
-
- public static class Builder {
-
- private int argumentIndex = -1;
- private boolean isAlwaysNull = false;
- private DexType type = null;
-
- public Builder setArgumentIndex(int argumentIndex) {
- this.argumentIndex = argumentIndex;
- return this;
- }
-
- public Builder setIsAlwaysNull() {
- this.isAlwaysNull = true;
- return this;
- }
-
- public Builder setType(DexType type) {
- this.type = type;
- return this;
- }
-
- public RemovedArgumentInfo build() {
- assert argumentIndex >= 0;
- assert type != null;
- return new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type);
- }
- }
-
- private final int argumentIndex;
- private final boolean isAlwaysNull;
- private final DexType type;
-
- private RemovedArgumentInfo(int argumentIndex, boolean isAlwaysNull, DexType type) {
- this.argumentIndex = argumentIndex;
- this.isAlwaysNull = isAlwaysNull;
- this.type = type;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public int getArgumentIndex() {
- return argumentIndex;
- }
-
- public DexType getType() {
- return type;
- }
-
- public boolean isAlwaysNull() {
- return isAlwaysNull;
- }
-
- public boolean isNeverUsed() {
- return !isAlwaysNull;
- }
-
- public RemovedArgumentInfo withArgumentIndex(int argumentIndex) {
- return this.argumentIndex != argumentIndex
- ? new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type)
- : this;
- }
- }
-
- public static class RemovedArgumentsInfo {
-
- private static final RemovedArgumentsInfo empty = new RemovedArgumentsInfo(null);
-
- private final List<RemovedArgumentInfo> removedArguments;
-
- public RemovedArgumentsInfo(List<RemovedArgumentInfo> removedArguments) {
- assert verifyRemovedArguments(removedArguments);
- this.removedArguments = removedArguments;
- }
-
- private static boolean verifyRemovedArguments(List<RemovedArgumentInfo> removedArguments) {
- if (removedArguments != null && !removedArguments.isEmpty()) {
- // Check that list is sorted by argument indices.
- int lastArgumentIndex = removedArguments.get(0).getArgumentIndex();
- for (int i = 1; i < removedArguments.size(); ++i) {
- int currentArgumentIndex = removedArguments.get(i).getArgumentIndex();
- assert lastArgumentIndex < currentArgumentIndex;
- lastArgumentIndex = currentArgumentIndex;
- }
- }
- return true;
- }
-
- public static RemovedArgumentsInfo empty() {
- return empty;
- }
-
- public ListIterator<RemovedArgumentInfo> iterator() {
- return removedArguments == null
- ? Collections.emptyListIterator()
- : removedArguments.listIterator();
- }
-
- public boolean hasRemovedArguments() {
- return removedArguments != null && !removedArguments.isEmpty();
- }
-
- public boolean isArgumentRemoved(int argumentIndex) {
- if (removedArguments != null) {
- for (RemovedArgumentInfo info : removedArguments) {
- if (info.getArgumentIndex() == argumentIndex) {
- return true;
- }
- }
- }
- return false;
- }
-
- public int numberOfRemovedArguments() {
- return removedArguments != null ? removedArguments.size() : 0;
- }
-
- public RemovedArgumentsInfo combine(RemovedArgumentsInfo info) {
- assert info != null;
- if (hasRemovedArguments()) {
- if (!info.hasRemovedArguments()) {
- return this;
- }
- } else {
- return info;
- }
-
- List<RemovedArgumentInfo> newRemovedArguments = new LinkedList<>(removedArguments);
- ListIterator<RemovedArgumentInfo> iterator = newRemovedArguments.listIterator();
- int offset = 0;
- for (RemovedArgumentInfo pending : info.removedArguments) {
- RemovedArgumentInfo next = IteratorUtils.peekNext(iterator);
- while (next != null && next.getArgumentIndex() <= pending.getArgumentIndex() + offset) {
- iterator.next();
- next = IteratorUtils.peekNext(iterator);
- offset++;
- }
- iterator.add(pending.withArgumentIndex(pending.getArgumentIndex() + offset));
- }
- return new RemovedArgumentsInfo(newRemovedArguments);
- }
-
- public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
- DexEncodedMethod method) {
- if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
- return builder -> {
- int firstArgumentIndex = BooleanUtils.intValue(!method.isStatic());
- builder.removeParameterAnnotations(
- oldIndex -> isArgumentRemoved(oldIndex + firstArgumentIndex));
- };
- }
- return null;
- }
- }
-
- private static final RewrittenPrototypeDescription none = new RewrittenPrototypeDescription();
-
- private final boolean hasBeenChangedToReturnVoid;
- private final boolean extraNullParameter;
- private final RemovedArgumentsInfo removedArgumentsInfo;
-
- private RewrittenPrototypeDescription() {
- this(false, false, RemovedArgumentsInfo.empty());
- }
-
- public RewrittenPrototypeDescription(
- boolean hasBeenChangedToReturnVoid,
- boolean extraNullParameter,
- RemovedArgumentsInfo removedArgumentsInfo) {
- assert removedArgumentsInfo != null;
- this.extraNullParameter = extraNullParameter;
- this.hasBeenChangedToReturnVoid = hasBeenChangedToReturnVoid;
- this.removedArgumentsInfo = removedArgumentsInfo;
- }
-
- public static RewrittenPrototypeDescription none() {
- return none;
- }
-
- public boolean isEmpty() {
- return !extraNullParameter
- && !hasBeenChangedToReturnVoid
- && !getRemovedArgumentsInfo().hasRemovedArguments();
- }
-
- public boolean hasExtraNullParameter() {
- return extraNullParameter;
- }
-
- public boolean hasBeenChangedToReturnVoid() {
- return hasBeenChangedToReturnVoid;
- }
-
- public RemovedArgumentsInfo getRemovedArgumentsInfo() {
- return removedArgumentsInfo;
- }
-
- /**
- * Returns the {@link ConstInstruction} that should be used to materialize the result of
- * invocations to the method represented by this {@link RewrittenPrototypeDescription}.
- *
- * <p>This method should only be used for methods that return a constant value and whose return
- * type has been changed to void.
- *
- * <p>Note that the current implementation always returns null at this point.
- */
- public ConstInstruction getConstantReturn(IRCode code, Position position) {
- assert hasBeenChangedToReturnVoid;
- ConstInstruction instruction = code.createConstNull();
- instruction.setPosition(position);
- return instruction;
- }
-
- public DexType rewriteReturnType(DexType returnType, DexItemFactory dexItemFactory) {
- return hasBeenChangedToReturnVoid ? dexItemFactory.voidType : returnType;
- }
-
- public DexType[] rewriteParameters(DexType[] params) {
- RemovedArgumentsInfo removedArgumentsInfo = getRemovedArgumentsInfo();
- if (removedArgumentsInfo.hasRemovedArguments()) {
- DexType[] newParams =
- new DexType[params.length - removedArgumentsInfo.numberOfRemovedArguments()];
- int newParamIndex = 0;
- for (int oldParamIndex = 0; oldParamIndex < params.length; ++oldParamIndex) {
- if (!removedArgumentsInfo.isArgumentRemoved(oldParamIndex)) {
- newParams[newParamIndex] = params[oldParamIndex];
- ++newParamIndex;
- }
- }
- return newParams;
- }
- return params;
- }
-
- public DexProto rewriteProto(DexProto proto, DexItemFactory dexItemFactory) {
- DexType newReturnType = rewriteReturnType(proto.returnType, dexItemFactory);
- DexType[] newParameters = rewriteParameters(proto.parameters.values);
- return dexItemFactory.createProto(newReturnType, newParameters);
- }
-
- public RewrittenPrototypeDescription withConstantReturn() {
- return !hasBeenChangedToReturnVoid
- ? new RewrittenPrototypeDescription(true, extraNullParameter, removedArgumentsInfo)
- : this;
- }
-
- public RewrittenPrototypeDescription withRemovedArguments(RemovedArgumentsInfo other) {
- return new RewrittenPrototypeDescription(
- hasBeenChangedToReturnVoid, extraNullParameter, removedArgumentsInfo.combine(other));
- }
-
- public RewrittenPrototypeDescription withExtraNullParameter() {
- return !extraNullParameter
- ? new RewrittenPrototypeDescription(
- hasBeenChangedToReturnVoid, true, removedArgumentsInfo)
- : this;
- }
- }
-
public static class Builder {
protected Builder() {}
@@ -594,7 +322,7 @@
if (isContextFreeForMethod(method)) {
result.put(lookupMethod(method), entry.getBooleanValue());
} else {
- for (DexMethod candidate: lookupMethodInAllContexts(method)) {
+ for (DexMethod candidate : lookupMethodInAllContexts(method)) {
result.put(candidate, entry.getBooleanValue());
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index 23f2a61..a25bab3 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -389,12 +389,7 @@
// public void bar() { }
// }
//
- if (resolvedMethod.hasCode()) {
- DexProgramClass holder = resolvedHolder.asProgramClass();
- if (appInfo.hasAnyInstantiatedLambdas(holder)) {
- result.add(resolvedMethod);
- }
- }
+ addIfDefaultMethodWithLambdaInstantiations(appInfo, resolvedMethod, result);
DexMethod method = resolvedMethod.method;
Consumer<DexEncodedMethod> addIfNotAbstract =
@@ -404,10 +399,8 @@
}
};
// Default methods are looked up when looking at a specific subtype that does not override
- // them.
- // Otherwise, we would look up default methods that are actually never used. However, we have
- // to
- // add bridge methods, otherwise we can remove a bridge that will be used.
+ // them. Otherwise, we would look up default methods that are actually never used. However, we
+ // have to add bridge methods, otherwise we can remove a bridge that will be used.
Consumer<DexEncodedMethod> addIfNotAbstractAndBridge =
m -> {
if (!m.accessFlags.isAbstract() && m.accessFlags.isBridge()) {
@@ -422,7 +415,11 @@
if (clazz.isInterface()) {
ResolutionResult targetMethods = appInfo.resolveMethodOnInterface(clazz, method);
if (targetMethods.isSingleResolution()) {
- addIfNotAbstractAndBridge.accept(targetMethods.getSingleTarget());
+ // Sub-interfaces can have default implementations that override the resolved method.
+ // Therefore we have to add default methods in sub interfaces.
+ DexEncodedMethod singleTarget = targetMethods.getSingleTarget();
+ addIfDefaultMethodWithLambdaInstantiations(appInfo, singleTarget, result);
+ addIfNotAbstractAndBridge.accept(singleTarget);
}
} else {
ResolutionResult targetMethods = appInfo.resolveMethodOnClass(clazz, method);
@@ -433,6 +430,19 @@
}
return result;
}
+
+ private void addIfDefaultMethodWithLambdaInstantiations(
+ AppInfoWithSubtyping appInfo, DexEncodedMethod method, Set<DexEncodedMethod> result) {
+ if (method == null) {
+ return;
+ }
+ if (method.hasCode()) {
+ DexProgramClass holder = appInfo.definitionForProgramType(method.method.holder);
+ if (appInfo.hasAnyInstantiatedLambdas(holder)) {
+ result.add(method);
+ }
+ }
+ }
}
abstract static class EmptyResult extends ResolutionResult {
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
new file mode 100644
index 0000000..5c22878
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -0,0 +1,289 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.ir.code.ConstInstruction;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.IteratorUtils;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.function.Consumer;
+
+public class RewrittenPrototypeDescription {
+
+ public static class RemovedArgumentInfo {
+
+ public static class Builder {
+
+ private int argumentIndex = -1;
+ private boolean isAlwaysNull = false;
+ private DexType type = null;
+
+ public Builder setArgumentIndex(int argumentIndex) {
+ this.argumentIndex = argumentIndex;
+ return this;
+ }
+
+ public Builder setIsAlwaysNull() {
+ this.isAlwaysNull = true;
+ return this;
+ }
+
+ public Builder setType(DexType type) {
+ this.type = type;
+ return this;
+ }
+
+ public RemovedArgumentInfo build() {
+ assert argumentIndex >= 0;
+ assert type != null;
+ return new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type);
+ }
+ }
+
+ private final int argumentIndex;
+ private final boolean isAlwaysNull;
+ private final DexType type;
+
+ private RemovedArgumentInfo(int argumentIndex, boolean isAlwaysNull, DexType type) {
+ this.argumentIndex = argumentIndex;
+ this.isAlwaysNull = isAlwaysNull;
+ this.type = type;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public int getArgumentIndex() {
+ return argumentIndex;
+ }
+
+ public DexType getType() {
+ return type;
+ }
+
+ public boolean isAlwaysNull() {
+ return isAlwaysNull;
+ }
+
+ public boolean isNeverUsed() {
+ return !isAlwaysNull;
+ }
+
+ RemovedArgumentInfo withArgumentIndex(int argumentIndex) {
+ return this.argumentIndex != argumentIndex
+ ? new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type)
+ : this;
+ }
+ }
+
+ public static class RemovedArgumentsInfo {
+
+ private static final RemovedArgumentsInfo empty = new RemovedArgumentsInfo(null);
+
+ private final List<RemovedArgumentInfo> removedArguments;
+
+ public RemovedArgumentsInfo(List<RemovedArgumentInfo> removedArguments) {
+ assert verifyRemovedArguments(removedArguments);
+ this.removedArguments = removedArguments;
+ }
+
+ private static boolean verifyRemovedArguments(List<RemovedArgumentInfo> removedArguments) {
+ if (removedArguments != null && !removedArguments.isEmpty()) {
+ // Check that list is sorted by argument indices.
+ int lastArgumentIndex = removedArguments.get(0).getArgumentIndex();
+ for (int i = 1; i < removedArguments.size(); ++i) {
+ int currentArgumentIndex = removedArguments.get(i).getArgumentIndex();
+ assert lastArgumentIndex < currentArgumentIndex;
+ lastArgumentIndex = currentArgumentIndex;
+ }
+ }
+ return true;
+ }
+
+ public static RemovedArgumentsInfo empty() {
+ return empty;
+ }
+
+ public ListIterator<RemovedArgumentInfo> iterator() {
+ return removedArguments == null
+ ? Collections.emptyListIterator()
+ : removedArguments.listIterator();
+ }
+
+ public boolean hasRemovedArguments() {
+ return removedArguments != null && !removedArguments.isEmpty();
+ }
+
+ public boolean isArgumentRemoved(int argumentIndex) {
+ if (removedArguments != null) {
+ for (RemovedArgumentInfo info : removedArguments) {
+ if (info.getArgumentIndex() == argumentIndex) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public DexType[] rewriteParameters(DexEncodedMethod encodedMethod) {
+ // Currently not allowed to remove the receiver of an instance method. This would involve
+ // changing invoke-direct/invoke-virtual into invoke-static.
+ assert encodedMethod.isStatic() || !isArgumentRemoved(0);
+ DexType[] params = encodedMethod.method.proto.parameters.values;
+ if (!hasRemovedArguments()) {
+ return params;
+ }
+ DexType[] newParams = new DexType[params.length - numberOfRemovedArguments()];
+ int offset = encodedMethod.isStatic() ? 0 : 1;
+ int newParamIndex = 0;
+ for (int oldParamIndex = 0; oldParamIndex < params.length; ++oldParamIndex) {
+ if (!isArgumentRemoved(oldParamIndex + offset)) {
+ newParams[newParamIndex++] = params[oldParamIndex];
+ }
+ }
+ return newParams;
+ }
+
+ public int numberOfRemovedArguments() {
+ return removedArguments != null ? removedArguments.size() : 0;
+ }
+
+ public RemovedArgumentsInfo combine(RemovedArgumentsInfo info) {
+ assert info != null;
+ if (hasRemovedArguments()) {
+ if (!info.hasRemovedArguments()) {
+ return this;
+ }
+ } else {
+ return info;
+ }
+
+ List<RemovedArgumentInfo> newRemovedArguments = new LinkedList<>(removedArguments);
+ ListIterator<RemovedArgumentInfo> iterator = newRemovedArguments.listIterator();
+ int offset = 0;
+ for (RemovedArgumentInfo pending : info.removedArguments) {
+ RemovedArgumentInfo next = IteratorUtils.peekNext(iterator);
+ while (next != null && next.getArgumentIndex() <= pending.getArgumentIndex() + offset) {
+ iterator.next();
+ next = IteratorUtils.peekNext(iterator);
+ offset++;
+ }
+ iterator.add(pending.withArgumentIndex(pending.getArgumentIndex() + offset));
+ }
+ return new RemovedArgumentsInfo(newRemovedArguments);
+ }
+
+ public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
+ DexEncodedMethod method) {
+ if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
+ return builder -> {
+ int firstArgumentIndex = BooleanUtils.intValue(!method.isStatic());
+ builder.removeParameterAnnotations(
+ oldIndex -> isArgumentRemoved(oldIndex + firstArgumentIndex));
+ };
+ }
+ return null;
+ }
+ }
+
+ private static final RewrittenPrototypeDescription none = new RewrittenPrototypeDescription();
+
+ private final boolean hasBeenChangedToReturnVoid;
+ private final boolean extraNullParameter;
+ private final RemovedArgumentsInfo removedArgumentsInfo;
+
+ private RewrittenPrototypeDescription() {
+ this(false, false, RemovedArgumentsInfo.empty());
+ }
+
+ private RewrittenPrototypeDescription(
+ boolean hasBeenChangedToReturnVoid,
+ boolean extraNullParameter,
+ RemovedArgumentsInfo removedArgumentsInfo) {
+ assert removedArgumentsInfo != null;
+ this.extraNullParameter = extraNullParameter;
+ this.hasBeenChangedToReturnVoid = hasBeenChangedToReturnVoid;
+ this.removedArgumentsInfo = removedArgumentsInfo;
+ }
+
+ public static RewrittenPrototypeDescription createForUninstantiatedTypes(
+ boolean hasBeenChangedToReturnVoid, RemovedArgumentsInfo removedArgumentsInfo) {
+ return new RewrittenPrototypeDescription(
+ hasBeenChangedToReturnVoid, false, removedArgumentsInfo);
+ }
+
+ public static RewrittenPrototypeDescription none() {
+ return none;
+ }
+
+ public boolean isEmpty() {
+ return !extraNullParameter
+ && !hasBeenChangedToReturnVoid
+ && !getRemovedArgumentsInfo().hasRemovedArguments();
+ }
+
+ public boolean hasExtraNullParameter() {
+ return extraNullParameter;
+ }
+
+ public boolean hasBeenChangedToReturnVoid() {
+ return hasBeenChangedToReturnVoid;
+ }
+
+ public RemovedArgumentsInfo getRemovedArgumentsInfo() {
+ return removedArgumentsInfo;
+ }
+
+ /**
+ * Returns the {@link ConstInstruction} that should be used to materialize the result of
+ * invocations to the method represented by this {@link RewrittenPrototypeDescription}.
+ *
+ * <p>This method should only be used for methods that return a constant value and whose return
+ * type has been changed to void.
+ *
+ * <p>Note that the current implementation always returns null at this point.
+ */
+ public ConstInstruction getConstantReturn(IRCode code, Position position) {
+ assert hasBeenChangedToReturnVoid;
+ ConstInstruction instruction = code.createConstNull();
+ instruction.setPosition(position);
+ return instruction;
+ }
+
+ public DexProto rewriteProto(DexEncodedMethod encodedMethod, DexItemFactory dexItemFactory) {
+ if (isEmpty()) {
+ return encodedMethod.method.proto;
+ }
+ DexType newReturnType =
+ hasBeenChangedToReturnVoid
+ ? dexItemFactory.voidType
+ : encodedMethod.method.proto.returnType;
+ DexType[] newParameters = removedArgumentsInfo.rewriteParameters(encodedMethod);
+ return dexItemFactory.createProto(newReturnType, newParameters);
+ }
+
+ public RewrittenPrototypeDescription withConstantReturn() {
+ return !hasBeenChangedToReturnVoid
+ ? new RewrittenPrototypeDescription(true, extraNullParameter, removedArgumentsInfo)
+ : this;
+ }
+
+ public RewrittenPrototypeDescription withRemovedArguments(RemovedArgumentsInfo other) {
+ return new RewrittenPrototypeDescription(
+ hasBeenChangedToReturnVoid, extraNullParameter, removedArgumentsInfo.combine(other));
+ }
+
+ public RewrittenPrototypeDescription withExtraNullParameter() {
+ return !extraNullParameter
+ ? new RewrittenPrototypeDescription(hasBeenChangedToReturnVoid, true, removedArgumentsInfo)
+ : this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
new file mode 100644
index 0000000..b463c75
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class FieldAccessAnalysis {
+
+ private final AppView<?> appView;
+ private final FieldAssignmentTracker fieldAssignmentTracker;
+ private final FieldBitAccessAnalysis fieldBitAccessAnalysis;
+
+ public FieldAccessAnalysis(AppView<AppInfoWithLiveness> appView) {
+ InternalOptions options = appView.options();
+ this.appView = appView;
+ this.fieldBitAccessAnalysis =
+ options.enableFieldBitAccessAnalysis ? new FieldBitAccessAnalysis() : null;
+ this.fieldAssignmentTracker =
+ options.enableFieldAssignmentTracker ? new FieldAssignmentTracker(appView) : null;
+ }
+
+ public FieldAccessAnalysis(
+ AppView<?> appView,
+ FieldAssignmentTracker fieldAssignmentTracker,
+ FieldBitAccessAnalysis fieldBitAccessAnalysis) {
+ this.appView = appView;
+ this.fieldAssignmentTracker = fieldAssignmentTracker;
+ this.fieldBitAccessAnalysis = fieldBitAccessAnalysis;
+ }
+
+ public static boolean enable(InternalOptions options) {
+ return options.enableFieldBitAccessAnalysis || options.enableFieldAssignmentTracker;
+ }
+
+ public FieldAssignmentTracker fieldAssignmentTracker() {
+ return fieldAssignmentTracker;
+ }
+
+ public void acceptClassInitializerDefaultsResult(
+ ClassInitializerDefaultsResult classInitializerDefaultsResult) {
+ if (fieldAssignmentTracker != null) {
+ fieldAssignmentTracker.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
+ }
+ }
+
+ public void recordFieldAccesses(
+ IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) {
+ if (!code.metadata().mayHaveFieldInstruction() || !methodProcessor.isPrimary()) {
+ return;
+ }
+
+ Iterable<FieldInstruction> fieldInstructions =
+ code.instructions(Instruction::isFieldInstruction);
+ for (FieldInstruction fieldInstruction : fieldInstructions) {
+ DexEncodedField encodedField = appView.appInfo().resolveField(fieldInstruction.getField());
+ if (encodedField != null && encodedField.isProgramField(appView)) {
+ if (fieldAssignmentTracker != null) {
+ fieldAssignmentTracker.recordFieldAccess(fieldInstruction, encodedField, code.method);
+ }
+ if (fieldBitAccessAnalysis != null) {
+ fieldBitAccessAnalysis.recordFieldAccess(fieldInstruction, encodedField, feedback);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
new file mode 100644
index 0000000..0b0b542
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -0,0 +1,142 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.FieldAccessInfoCollection;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
+import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class FieldAssignmentTracker {
+
+ private final AppView<AppInfoWithLiveness> appView;
+
+ // A field access graph with edges from methods to the fields that they access. Edges are removed
+ // from the graph as we process methods, such that we can conclude that all field writes have been
+ // processed when a field no longer has any incoming edges.
+ private final FieldAccessGraph fieldAccessGraph;
+
+ // The set of fields that may store a non-zero value.
+ private final Set<DexEncodedField> nonZeroFields = Sets.newConcurrentHashSet();
+
+ FieldAssignmentTracker(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ this.fieldAccessGraph = new FieldAccessGraph(appView);
+ }
+
+ private boolean isAlwaysZero(DexEncodedField field) {
+ return !appView.appInfo().isPinned(field.field) && !nonZeroFields.contains(field);
+ }
+
+ void acceptClassInitializerDefaultsResult(
+ ClassInitializerDefaultsResult classInitializerDefaultsResult) {
+ classInitializerDefaultsResult.forEachOptimizedField(
+ (field, value) -> {
+ if (!value.isDefault(field.field.type)) {
+ nonZeroFields.add(field);
+ }
+ });
+ }
+
+ void recordFieldAccess(
+ FieldInstruction instruction, DexEncodedField field, DexEncodedMethod context) {
+ if (instruction.isFieldPut()) {
+ recordFieldPut(field, instruction.value(), context);
+ }
+ }
+
+ private void recordFieldPut(DexEncodedField field, Value value, DexEncodedMethod context) {
+ assert verifyValueIsConsistentWithFieldOptimizationInfo(
+ value, field.getOptimizationInfo(), context);
+ if (!value.isZero()) {
+ nonZeroFields.add(field);
+ }
+ }
+
+ private void recordAllFieldPutsProcessed(DexEncodedField field, OptimizationFeedback feedback) {
+ if (isAlwaysZero(field)) {
+ feedback.recordFieldHasAbstractValue(
+ field, appView, appView.abstractValueFactory().createSingleNumberValue(0));
+ }
+ }
+
+ public void waveDone(Collection<DexEncodedMethod> wave, OptimizationFeedback feedback) {
+ for (DexEncodedMethod method : wave) {
+ fieldAccessGraph.markProcessed(method, field -> recordAllFieldPutsProcessed(field, feedback));
+ }
+ }
+
+ private boolean verifyValueIsConsistentWithFieldOptimizationInfo(
+ Value value, FieldOptimizationInfo optimizationInfo, DexEncodedMethod context) {
+ AbstractValue abstractValue = optimizationInfo.getAbstractValue();
+ if (abstractValue.isUnknown()) {
+ return true;
+ }
+ assert abstractValue == value.getAbstractValue(appView, context.method.holder);
+ return true;
+ }
+
+ static class FieldAccessGraph {
+
+ // The fields written by each method.
+ private final Map<DexEncodedMethod, List<DexEncodedField>> fieldWrites =
+ new IdentityHashMap<>();
+
+ // The number of writes that have not yet been processed per field.
+ private final Reference2IntMap<DexEncodedField> pendingFieldWrites =
+ new Reference2IntOpenHashMap<>();
+
+ FieldAccessGraph(AppView<AppInfoWithLiveness> appView) {
+ FieldAccessInfoCollection<?> fieldAccessInfoCollection =
+ appView.appInfo().getFieldAccessInfoCollection();
+ fieldAccessInfoCollection.flattenAccessContexts();
+ fieldAccessInfoCollection.forEach(
+ info -> {
+ DexEncodedField field = appView.appInfo().resolveField(info.getField());
+ if (field == null) {
+ assert false;
+ return;
+ }
+ if (!info.hasReflectiveAccess()) {
+ info.forEachWriteContext(
+ context ->
+ fieldWrites.computeIfAbsent(context, ignore -> new ArrayList<>()).add(field));
+ pendingFieldWrites.put(field, info.getNumberOfWriteContexts());
+ }
+ });
+ }
+
+ void markProcessed(DexEncodedMethod method, Consumer<DexEncodedField> allWritesSeenConsumer) {
+ List<DexEncodedField> fieldWritesInMethod = fieldWrites.get(method);
+ if (fieldWritesInMethod != null) {
+ for (DexEncodedField field : fieldWritesInMethod) {
+ int numberOfPendingFieldWrites = pendingFieldWrites.removeInt(field) - 1;
+ if (numberOfPendingFieldWrites > 0) {
+ pendingFieldWrites.put(field, numberOfPendingFieldWrites);
+ } else {
+ allWritesSeenConsumer.accept(field);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessAnalysis.java
index deabda7..a178a69 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessAnalysis.java
@@ -4,13 +4,9 @@
package com.android.tools.r8.ir.analysis.fieldaccess;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.ir.code.And;
import com.android.tools.r8.ir.code.FieldInstruction;
-import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.LogicalBinop;
import com.android.tools.r8.ir.code.Value;
@@ -19,39 +15,18 @@
public class FieldBitAccessAnalysis {
- private final AppView<? extends AppInfoWithSubtyping> appView;
-
- public FieldBitAccessAnalysis(AppView<? extends AppInfoWithSubtyping> appView) {
- assert appView.enableWholeProgramOptimizations();
- this.appView = appView;
- }
-
- public void recordFieldAccesses(IRCode code, OptimizationFeedback feedback) {
- if (!code.metadata().mayHaveFieldInstruction()) {
+ public void recordFieldAccess(
+ FieldInstruction instruction, DexEncodedField field, OptimizationFeedback feedback) {
+ if (!field.field.type.isIntType()) {
return;
}
- for (Instruction instruction : code.instructions()) {
- if (instruction.isFieldInstruction()) {
- FieldInstruction fieldInstruction = instruction.asFieldInstruction();
- DexField field = fieldInstruction.getField();
- if (!field.type.isIntType()) {
- continue;
- }
+ if (BitAccessInfo.allBitsRead(field.getOptimizationInfo().getReadBits())) {
+ return;
+ }
- DexEncodedField encodedField = appView.appInfo().resolveField(field);
- if (encodedField == null || !encodedField.isProgramField(appView)) {
- continue;
- }
-
- if (BitAccessInfo.allBitsRead(encodedField.getOptimizationInfo().getReadBits())) {
- continue;
- }
-
- if (fieldInstruction.isFieldGet()) {
- feedback.markFieldBitsRead(encodedField, computeBitsRead(fieldInstruction, encodedField));
- }
- }
+ if (instruction.isFieldGet()) {
+ feedback.markFieldBitsRead(field, computeBitsRead(instruction, field));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index 0b4bb94..4de5c39 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -99,6 +99,7 @@
*/
public TreePrunerConfiguration run(Enqueuer.Mode mode) {
forEachDeadProtoExtensionField(this::recordDeadProtoExtensionField);
+ appView.appInfo().getFieldAccessInfoCollection().removeIf((field, info) -> wasRemoved(field));
return createTreePrunerConfiguration(mode);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 820789a..4ff6283 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -350,13 +350,13 @@
// (i) optimize field reads into loading the default value of the field or (ii) remove
// field writes to proto fields that could be read using reflection by the proto
// library.
- enqueuer.registerFieldAccess(valueStorage.field, dynamicMethod);
+ enqueuer.registerReflectiveFieldAccess(valueStorage.field, dynamicMethod);
}
valueStorageIsLive = true;
} else if (reachesMapOrRequiredField(protoFieldInfo)) {
// Map/required fields cannot be removed. Therefore, we mark such fields as both read and
// written such that we cannot optimize any field reads or writes.
- enqueuer.registerFieldAccess(valueStorage.field, dynamicMethod);
+ enqueuer.registerReflectiveFieldAccess(valueStorage.field, dynamicMethod);
worklist.enqueueMarkReachableFieldAction(
clazz, valueStorage, KeepReason.reflectiveUseIn(dynamicMethod));
valueStorageIsLive = true;
@@ -411,12 +411,12 @@
writer -> writer != defaultInitializer && writer != dynamicMethod;
if (enqueuer.isFieldWrittenInMethodSatisfying(
newlyLiveField, neitherDefaultConstructorNorDynamicMethod)) {
- enqueuer.registerFieldRead(newlyLiveField.field, dynamicMethod);
+ enqueuer.registerReflectiveFieldRead(newlyLiveField.field, dynamicMethod);
}
// Unconditionally register the hazzer and one-of proto fields as written from
// dynamicMethod().
- if (enqueuer.registerFieldWrite(newlyLiveField.field, dynamicMethod)) {
+ if (enqueuer.registerReflectiveFieldWrite(newlyLiveField.field, dynamicMethod)) {
worklist.enqueueMarkReachableFieldAction(
clazz, newlyLiveField, KeepReason.reflectiveUseIn(dynamicMethod));
}
@@ -527,7 +527,7 @@
return;
}
- if (enqueuer.registerFieldWrite(encodedOneOfField.field, dynamicMethod)) {
+ if (enqueuer.registerReflectiveFieldWrite(encodedOneOfField.field, dynamicMethod)) {
worklist.enqueueMarkReachableFieldAction(
clazz.asProgramClass(), encodedOneOfField, KeepReason.reflectiveUseIn(dynamicMethod));
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index 2bd87e0..c70ff33 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -8,6 +8,10 @@
public abstract boolean isNonTrivial();
+ public boolean isZero() {
+ return false;
+ }
+
/**
* Returns true if this abstract value represents a single concrete value (i.e., the
* concretization of this abstract value has size 1).
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index 9a43d23..4a5ad60 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -25,6 +25,11 @@
}
@Override
+ public boolean isZero() {
+ return value == 0;
+ }
+
+ @Override
public boolean isSingleNumberValue() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 2e5467c..01dcf5d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -40,8 +40,8 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentsInfo;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.CanonicalPositions;
@@ -158,7 +158,8 @@
int register,
DexEncodedMethod method,
BiConsumer<Integer, DexType> writeCallback) {
- RemovedArgumentsInfo removedArgumentsInfo = builder.prototypeChanges.getRemovedArgumentsInfo();
+ RemovedArgumentsInfo removedArgumentsInfo =
+ builder.getPrototypeChanges().getRemovedArgumentsInfo();
ListIterator<RemovedArgumentInfo> removedArgumentIterator = removedArgumentsInfo.iterator();
RemovedArgumentInfo nextRemovedArgument =
removedArgumentIterator.hasNext() ? removedArgumentIterator.next() : null;
@@ -186,7 +187,7 @@
type =
TypeLatticeElement.fromDexType(
nextRemovedArgument.getType(), Nullability.maybeNull(), builder.appView);
- builder.addConstantOrUnusedArgument(register);
+ builder.addConstantOrUnusedArgument(register, nextRemovedArgument);
nextRemovedArgument =
removedArgumentIterator.hasNext() ? removedArgumentIterator.next() : null;
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 42728d4..3d0fed3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -34,8 +34,8 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -113,7 +113,6 @@
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -133,7 +132,6 @@
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
@@ -392,9 +390,7 @@
private DexEncodedMethod context;
public final AppView<?> appView;
private final Origin origin;
- final RewrittenPrototypeDescription prototypeChanges;
- private ListIterator<RemovedArgumentInfo> removedArgumentsIterator;
- private int argumentCount = 0;
+ private final RewrittenPrototypeDescription prototypeChanges;
private Value receiverValue;
private List<Value> argumentValues;
@@ -461,7 +457,6 @@
+ method.toSourceString());
}
}
- this.removedArgumentsIterator = this.prototypeChanges.getRemovedArgumentsInfo().iterator();
}
private static boolean verifyMethodSignature(DexEncodedMethod method, GraphLense graphLense) {
@@ -476,6 +471,10 @@
return method;
}
+ public RewrittenPrototypeDescription getPrototypeChanges() {
+ return prototypeChanges;
+ }
+
public boolean isDebugMode() {
return appView.options().debug || method.getOptimizationInfo().isReachabilitySensitive();
}
@@ -854,16 +853,6 @@
addInstruction(ir);
}
- private RemovedArgumentInfo getRemovedArgumentInfo() {
- RemovedArgumentInfo removedArgumentInfo = IteratorUtils.peekNext(removedArgumentsIterator);
- if (removedArgumentInfo != null && removedArgumentInfo.getArgumentIndex() == argumentCount) {
- argumentCount++;
- return removedArgumentsIterator.next();
- }
- argumentCount++;
- return null;
- }
-
void addThisArgument(int register) {
boolean receiverCouldBeNull = context != null && context != method;
Nullability nullability = receiverCouldBeNull ? maybeNull() : definitelyNotNull();
@@ -873,8 +862,6 @@
}
public void addThisArgument(int register, TypeLatticeElement receiverType) {
- RemovedArgumentInfo removedArgumentInfo = getRemovedArgumentInfo();
- assert removedArgumentInfo == null; // Removal of receiver not yet supported.
DebugLocalInfo local = getOutgoingLocal(register);
Value value = writeRegister(register, receiverType, ThrowingInfo.NO_THROW, local);
addInstruction(new Argument(value, false));
@@ -883,25 +870,15 @@
}
public void addNonThisArgument(int register, TypeLatticeElement typeLattice) {
- RemovedArgumentInfo removedArgumentInfo = getRemovedArgumentInfo();
- if (removedArgumentInfo == null) {
DebugLocalInfo local = getOutgoingLocal(register);
Value value = writeRegister(register, typeLattice, ThrowingInfo.NO_THROW, local);
addNonThisArgument(new Argument(value, false));
- } else {
- handleConstantOrUnusedArgument(register, removedArgumentInfo);
- }
}
public void addBooleanNonThisArgument(int register) {
- RemovedArgumentInfo removedArgumentInfo = getRemovedArgumentInfo();
- if (removedArgumentInfo == null) {
DebugLocalInfo local = getOutgoingLocal(register);
Value value = writeRegister(register, getInt(), ThrowingInfo.NO_THROW, local);
addNonThisArgument(new Argument(value, true));
- } else {
- assert removedArgumentInfo.isNeverUsed();
- }
}
private void addNonThisArgument(Argument argument) {
@@ -912,8 +889,8 @@
argumentValues.add(argument.outValue());
}
- public void addConstantOrUnusedArgument(int register) {
- handleConstantOrUnusedArgument(register, getRemovedArgumentInfo());
+ public void addConstantOrUnusedArgument(int register, RemovedArgumentInfo info) {
+ handleConstantOrUnusedArgument(register, info);
}
private void handleConstantOrUnusedArgument(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 2c9a53c..7800639 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -26,7 +26,7 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
-import com.android.tools.r8.ir.analysis.fieldaccess.FieldBitAccessAnalysis;
+import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.FieldValueAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.AlwaysMaterializingDefinition;
@@ -128,7 +128,7 @@
private final Timing timing;
private final Outliner outliner;
private final ClassInitializerDefaultsOptimization classInitializerDefaultsOptimization;
- private final FieldBitAccessAnalysis fieldBitAccessAnalysis;
+ private final FieldAccessAnalysis fieldAccessAnalysis;
private final LibraryMethodOptimizer libraryMethodOptimizer;
private final LibraryMethodOverrideAnalysis libraryMethodOverrideAnalysis;
private final StringConcatRewriter stringConcatRewriter;
@@ -232,7 +232,7 @@
this.dynamicTypeOptimization = null;
this.classInliner = null;
this.classStaticizer = null;
- this.fieldBitAccessAnalysis = null;
+ this.fieldAccessAnalysis = null;
this.libraryMethodOptimizer = null;
this.libraryMethodOverrideAnalysis = null;
this.inliner = null;
@@ -290,10 +290,8 @@
if (dynamicTypeOptimization != null) {
assumers.add(dynamicTypeOptimization);
}
- this.fieldBitAccessAnalysis =
- options.enableFieldBitAccessAnalysis
- ? new FieldBitAccessAnalysis(appViewWithLiveness)
- : null;
+ this.fieldAccessAnalysis =
+ FieldAccessAnalysis.enable(options) ? new FieldAccessAnalysis(appViewWithLiveness) : null;
this.libraryMethodOptimizer = new LibraryMethodOptimizer(appViewWithLiveness);
this.libraryMethodOverrideAnalysis =
options.enableTreeShakingOfLibraryMethodOverrides
@@ -331,7 +329,7 @@
this.classInliner = null;
this.classStaticizer = null;
this.dynamicTypeOptimization = null;
- this.fieldBitAccessAnalysis = null;
+ this.fieldAccessAnalysis = null;
this.libraryMethodOptimizer = null;
this.libraryMethodOverrideAnalysis = null;
this.inliner = null;
@@ -805,12 +803,14 @@
return builder.build();
}
- private void waveStart(Collection<DexEncodedMethod> wave, ExecutorService executorService)
- throws ExecutionException {
+ private void waveStart(Collection<DexEncodedMethod> wave) {
onWaveDoneActions = Collections.synchronizedList(new ArrayList<>());
}
- private void waveDone() {
+ private void waveDone(Collection<DexEncodedMethod> wave) {
+ if (options.enableFieldAssignmentTracker) {
+ fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
+ }
delayedOptimizationFeedback.updateVisibleOptimizationInfo();
onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
onWaveDoneActions = null;
@@ -1143,6 +1143,8 @@
// we will return with finalizeEmptyThrowingCode() above.
assert code.verifyTypes(appView);
+ assertionsRewriter.run(method, code, timing);
+
if (serviceLoaderRewriter != null) {
assert appView.appInfo().hasLiveness();
timing.begin("Rewrite service loaders");
@@ -1170,8 +1172,6 @@
timing.end();
}
- assertionsRewriter.run(method, code, timing);
-
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
timing.begin("Insert assume instructions");
@@ -1493,9 +1493,12 @@
timing.end();
}
- if (fieldBitAccessAnalysis != null) {
- timing.begin("Record field access");
- fieldBitAccessAnalysis.recordFieldAccesses(code, feedback);
+ if (fieldAccessAnalysis != null) {
+ timing.begin("Analyze field accesses");
+ fieldAccessAnalysis.recordFieldAccesses(code, feedback, methodProcessor);
+ if (classInitializerDefaultsResult != null) {
+ fieldAccessAnalysis.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
+ }
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index b65b164..246d865 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -27,9 +27,9 @@
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentsInfo;
import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
index 6c42599..acba17d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.ir.conversion.CallGraphBuilderBase.CycleEliminator;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.IROrdering;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
@@ -35,8 +34,7 @@
interface WaveStartAction {
- void notifyWaveStart(Collection<DexEncodedMethod> wave, ExecutorService executorService)
- throws ExecutionException;
+ void notifyWaveStart(Collection<DexEncodedMethod> wave);
}
private final CallSiteInformation callSiteInformation;
@@ -141,7 +139,7 @@
<E extends Exception> void forEachMethod(
ThrowingFunction<DexEncodedMethod, Timing, E> consumer,
WaveStartAction waveStartAction,
- Action waveDone,
+ Consumer<Collection<DexEncodedMethod>> waveDone,
Timing timing,
ExecutorService executorService)
throws ExecutionException {
@@ -150,7 +148,7 @@
while (!waves.isEmpty()) {
wave = waves.removeFirst();
assert wave.size() > 0;
- waveStartAction.notifyWaveStart(wave, executorService);
+ waveStartAction.notifyWaveStart(wave);
merger.add(
ThreadUtils.processItemsWithResults(
wave,
@@ -160,7 +158,7 @@
return time;
},
executorService));
- waveDone.execute();
+ waveDone.accept(wave);
}
merger.end();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
index 9fca058..78d5702 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.Invoke;
import com.google.common.collect.ImmutableMap;
import java.util.IdentityHashMap;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index 52d047e..e35087a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -56,6 +56,7 @@
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
import java.util.stream.Collectors;
public class ClassInitializerDefaultsOptimization {
@@ -75,6 +76,12 @@
return EMPTY;
}
+ public void forEachOptimizedField(BiConsumer<DexEncodedField, DexValue> consumer) {
+ if (fieldsWithStaticValues != null) {
+ fieldsWithStaticValues.forEach(consumer);
+ }
+ }
+
public boolean hasStaticValue(DexEncodedField field) {
if (field.isStatic()) {
return (fieldsWithStaticValues != null && fieldsWithStaticValues.containsKey(field))
@@ -86,14 +93,14 @@
private class WaveDoneAction implements Action {
- private final Map<DexEncodedField, DexValue> fieldsWithStaticValues;
- private final Set<DexField> noLongerWrittenFields;
+ private final Map<DexEncodedField, DexValue> fieldsWithStaticValues = new IdentityHashMap<>();
+ private final Set<DexField> noLongerWrittenFields = Sets.newIdentityHashSet();
public WaveDoneAction(
Map<DexEncodedField, DexValue> fieldsWithStaticValues,
Set<DexField> noLongerWrittenFields) {
- this.fieldsWithStaticValues = fieldsWithStaticValues;
- this.noLongerWrittenFields = noLongerWrittenFields;
+ this.fieldsWithStaticValues.putAll(fieldsWithStaticValues);
+ this.noLongerWrittenFields.addAll(noLongerWrittenFields);
}
public synchronized void join(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index e17e419..ea419cc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1469,6 +1469,8 @@
@Override
public void buildPrelude(IRBuilder builder) {
+ // If the assertion fails, check DexSourceCode#buildArgumentsWithUnusedArgumentStubs.
+ assert builder.getPrototypeChanges().isEmpty();
// Fill in the Argument instructions in the argument block.
for (int i = 0; i < outline.argumentTypes.size(); i++) {
if (outline.argumentTypes.get(i).isBooleanType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 5088d99..b871461 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -19,9 +19,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentsInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
import com.android.tools.r8.ir.analysis.AbstractError;
import com.android.tools.r8.ir.analysis.TypeChecker;
@@ -160,9 +160,8 @@
// This achieved by faking that there is already a method with the given signature.
for (DexEncodedMethod virtualMethod : clazz.virtualMethods()) {
RewrittenPrototypeDescription prototypeChanges =
- new RewrittenPrototypeDescription(
+ RewrittenPrototypeDescription.createForUninstantiatedTypes(
virtualMethod.method.proto.returnType.isAlwaysNull(appView),
- false,
getRemovedArgumentsInfo(virtualMethod, ALLOW_ARGUMENT_REMOVAL));
if (!prototypeChanges.isEmpty()) {
DexMethod newMethod = getNewMethodSignature(virtualMethod, prototypeChanges);
@@ -296,9 +295,8 @@
|| appView.appInfo().keepConstantArguments.contains(encodedMethod.method)) {
return RewrittenPrototypeDescription.none();
}
- return new RewrittenPrototypeDescription(
+ return RewrittenPrototypeDescription.createForUninstantiatedTypes(
encodedMethod.method.proto.returnType.isAlwaysNull(appView),
- false,
getRemovedArgumentsInfo(encodedMethod, strategy));
}
@@ -333,41 +331,10 @@
private DexMethod getNewMethodSignature(
DexEncodedMethod encodedMethod, RewrittenPrototypeDescription prototypeChanges) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
-
DexMethod method = encodedMethod.method;
- RemovedArgumentsInfo removedArgumentsInfo = prototypeChanges.getRemovedArgumentsInfo();
+ DexProto newProto = prototypeChanges.rewriteProto(encodedMethod, dexItemFactory);
- if (prototypeChanges.isEmpty()) {
- return method;
- }
-
- DexType newReturnType =
- prototypeChanges.hasBeenChangedToReturnVoid()
- ? dexItemFactory.voidType
- : method.proto.returnType;
-
- DexType[] newParameters;
- if (removedArgumentsInfo.hasRemovedArguments()) {
- // Currently not allowed to remove the receiver of an instance method. This would involve
- // changing invoke-direct/invoke-virtual into invoke-static.
- assert encodedMethod.isStatic() || !removedArgumentsInfo.isArgumentRemoved(0);
- newParameters =
- new DexType
- [method.proto.parameters.size() - removedArgumentsInfo.numberOfRemovedArguments()];
- int offset = encodedMethod.isStatic() ? 0 : 1;
- int newParametersIndex = 0;
- for (int argumentIndex = 0; argumentIndex < method.proto.parameters.size(); ++argumentIndex) {
- if (!removedArgumentsInfo.isArgumentRemoved(argumentIndex + offset)) {
- newParameters[newParametersIndex] = method.proto.parameters.values[argumentIndex];
- newParametersIndex++;
- }
- }
- } else {
- newParameters = method.proto.parameters.values;
- }
-
- return dexItemFactory.createMethod(
- method.holder, dexItemFactory.createProto(newReturnType, newParameters), method.name);
+ return dexItemFactory.createMethod(method.holder, newProto, method.name);
}
public void rewrite(IRCode code) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index aec5219..705b2bd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -16,8 +16,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentsInfo;
import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
@@ -329,25 +330,8 @@
private DexProto createProtoWithRemovedArguments(
DexEncodedMethod encodedMethod, RemovedArgumentsInfo unused) {
- DexMethod method = encodedMethod.method;
-
- int firstArgumentIndex = encodedMethod.isStatic() ? 0 : 1;
- int numberOfParameters = method.proto.parameters.size() - unused.numberOfRemovedArguments();
- if (!encodedMethod.isStatic() && unused.isArgumentRemoved(0)) {
- numberOfParameters++;
- }
-
- DexType[] parameters = new DexType[numberOfParameters];
- if (numberOfParameters > 0) {
- int newIndex = 0;
- for (int oldIndex = 0; oldIndex < method.proto.parameters.size(); oldIndex++) {
- if (!unused.isArgumentRemoved(oldIndex + firstArgumentIndex)) {
- parameters[newIndex++] = method.proto.parameters.values[oldIndex];
- }
- }
- assert newIndex == parameters.length;
- }
- return appView.dexItemFactory().createProto(method.proto.returnType, parameters);
+ DexType[] parameters = unused.rewriteParameters(encodedMethod);
+ return appView.dexItemFactory().createProto(encodedMethod.method.proto.returnType, parameters);
}
private static class CollectUsedArguments extends ArgumentUse {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
index 91e952a..67a7588 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
@@ -32,6 +32,7 @@
public LibraryMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
this.appView = appView;
register(new BooleanMethodOptimizer(appView));
+ register(new StringMethodOptimizer(appView));
if (LogMethodOptimizer.isEnabled(appView)) {
register(new LogMethodOptimizer(appView));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
new file mode 100644
index 0000000..5e4033b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.DexItemBasedConstString;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+public class StringMethodOptimizer implements LibraryMethodModelCollection {
+
+ private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final DexItemFactory dexItemFactory;
+
+ StringMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ @Override
+ public DexType getType() {
+ return dexItemFactory.stringType;
+ }
+
+ @Override
+ public void optimize(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues) {
+ if (singleTarget.method == dexItemFactory.stringMethods.equals) {
+ optimizeEquals(code, instructionIterator, invoke);
+ }
+ }
+
+ private void optimizeEquals(
+ IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
+ if (appView.appInfo().hasLiveness()) {
+ DexType context = code.method.method.holder;
+ Value first = invoke.arguments().get(0).getAliasedValue();
+ Value second = invoke.arguments().get(1).getAliasedValue();
+ if (isPrunedClassNameComparison(first, second, context)
+ || isPrunedClassNameComparison(second, first, context)) {
+ instructionIterator.replaceCurrentInstructionWithConstInt(appView, code, 0);
+ }
+ }
+ }
+
+ /**
+ * Returns true if {@param classNameValue} is defined by calling {@link Class#getName()} and
+ * {@param constStringValue} is a constant string that is identical to the name of a class that
+ * has been pruned by the {@link com.android.tools.r8.shaking.Enqueuer}.
+ */
+ private boolean isPrunedClassNameComparison(
+ Value classNameValue, Value constStringValue, DexType context) {
+ if (classNameValue.isPhi() || constStringValue.isPhi()) {
+ return false;
+ }
+
+ Instruction classNameDefinition = classNameValue.definition;
+ if (!classNameDefinition.isInvokeVirtual()) {
+ return false;
+ }
+
+ DexEncodedMethod singleTarget =
+ classNameDefinition.asInvokeVirtual().lookupSingleTarget(appView, context);
+ if (singleTarget == null || singleTarget.method != dexItemFactory.classMethods.getName) {
+ return false;
+ }
+
+ if (!constStringValue.definition.isDexItemBasedConstString()) {
+ return false;
+ }
+
+ DexItemBasedConstString constString = constStringValue.definition.asDexItemBasedConstString();
+ DexReference reference = constString.getItem();
+ return reference.isDexType()
+ && appView.appInfo().withLiveness().wasPruned(reference.asDexType())
+ && !constString.getNameComputationInfo().needsToComputeName();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index 8b662b9..00b5315 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.naming.MethodNameMinifier.State;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DisjointSets;
@@ -241,6 +242,7 @@
private final Set<DexCallSite> callSites = new HashSet<>();
private final Map<DexMethod, Set<InterfaceReservationState>> methodStates = new HashMap<>();
+ private final List<DexMethod> callSiteCollidingMethods = new ArrayList<>();
void addState(DexMethod method, InterfaceReservationState interfaceState) {
methodStates.computeIfAbsent(method, m -> new HashSet<>()).add(interfaceState);
@@ -248,6 +250,7 @@
void appendMethodGroupState(InterfaceMethodGroupState state) {
callSites.addAll(state.callSites);
+ callSiteCollidingMethods.addAll(state.callSiteCollidingMethods);
for (DexMethod key : state.methodStates.keySet()) {
methodStates.computeIfAbsent(key, k -> new HashSet<>()).addAll(state.methodStates.get(key));
}
@@ -455,6 +458,37 @@
groupState.addCallSite(callSite);
callSiteMethods.add(wrapped);
}
+ if (callSiteMethods.isEmpty()) {
+ return;
+ }
+ // For intersection types, we have to iterate all the multiple interfaces to look for
+ // methods with the same signature.
+ List<DexType> implementedInterfaces =
+ LambdaDescriptor.getInterfaces(callSite, appView.appInfo());
+ if (implementedInterfaces != null) {
+ for (int i = 1; i < implementedInterfaces.size(); i++) {
+ // Add the merging state for all additional implemented interfaces into the state
+ // for the group, if the name is different, to ensure that we do not pick the same
+ // name.
+ DexClass iface = appView.definitionFor(implementedInterfaces.get(i));
+ assert iface.isInterface();
+ for (DexEncodedMethod implementedMethod : implementedMethods) {
+ for (DexEncodedMethod virtualMethod : iface.virtualMethods()) {
+ boolean differentName =
+ !implementedMethod.method.name.equals(virtualMethod.method.name);
+ if (differentName
+ && MethodJavaSignatureEquivalence.getEquivalenceIgnoreName()
+ .equivalent(implementedMethod.method, virtualMethod.method)) {
+ InterfaceMethodGroupState interfaceMethodGroupState =
+ globalStateMap.computeIfAbsent(
+ equivalence.wrap(implementedMethod.method),
+ k -> new InterfaceMethodGroupState());
+ interfaceMethodGroupState.callSiteCollidingMethods.add(virtualMethod.method);
+ }
+ }
+ }
+ }
+ }
if (callSiteMethods.size() > 1) {
// Implemented interfaces have different protos. Unify them.
Wrapper<DexMethod> mainKey = callSiteMethods.iterator().next();
@@ -545,6 +579,27 @@
callSiteRenamings.put(callSite, newName);
}
}
+
+ // After all naming is completed for callsites, we must ensure to rename all interface methods
+ // that can collide with the callsite method name.
+ for (Wrapper<DexMethod> interfaceMethodGroup : nonReservedMethodGroups) {
+ InterfaceMethodGroupState groupState = globalStateMap.get(interfaceMethodGroup);
+ if (groupState.callSiteCollidingMethods.isEmpty()) {
+ continue;
+ }
+ DexMethod key = interfaceMethodGroup.get();
+ MethodNamingState<?> keyNamingState = minifierState.getNamingState(key.holder);
+ DexString existingRenaming = keyNamingState.newOrReservedNameFor(key);
+ assert existingRenaming != null;
+ for (DexMethod collidingMethod : groupState.callSiteCollidingMethods) {
+ DexString newNameInGroup = newNameInGroup(collidingMethod, keyNamingState, groupState);
+ minifierState.putRenaming(collidingMethod, newNameInGroup);
+ MethodNamingState<?> methodNamingState =
+ minifierState.getNamingState(collidingMethod.holder);
+ methodNamingState.addRenaming(newNameInGroup, collidingMethod);
+ keyNamingState.addRenaming(newNameInGroup, collidingMethod);
+ }
+ }
timing.end();
timing.end(); // end compute timing
@@ -563,6 +618,12 @@
return newName;
}
+ private DexString newNameInGroup(
+ DexMethod method, MethodNamingState<?> namingState, InterfaceMethodGroupState groupState) {
+ // Check if the name is available in all states.
+ return namingState.nextName(method, (candidate, ignore) -> groupState.isAvailable(candidate));
+ }
+
private void patchUpChildrenInReservationStates() {
for (Map.Entry<DexType, InterfaceReservationState> entry : interfaceStateMap.entrySet()) {
for (DexType parent : entry.getValue().iface.interfaces.values) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
index 7926de0..85c2f0f 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
@@ -62,8 +62,12 @@
return candidate;
}
}
+ return nextName(method, isAvailable);
+ }
+
+ DexString nextName(DexMethod method, BiPredicate<DexString, DexMethod> isAvailable) {
InternalNewNameState internalState = getOrCreateInternalState(method);
- newName = namingStrategy.next(method, internalState, isAvailable);
+ DexString newName = namingStrategy.next(method, internalState, isAvailable);
assert newName != null;
return newName;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index b90d37e..cd9db29 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -113,7 +113,6 @@
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
import org.objectweb.asm.Opcodes;
/**
@@ -411,34 +410,6 @@
}
}
- private Set<DexField> getNonPinnedWrittenFields(Predicate<DexEncodedField> predicate) {
- Set<DexField> result = Sets.newIdentityHashSet();
- fieldAccessInfoCollection.forEach(
- info -> {
- if (info == MISSING_FIELD_ACCESS_INFO) {
- return;
- }
- // Note that it is safe to use definitionFor() here, and not lookupField(), since the
- // field held by `info` is a direct reference to the definition of the field.
- DexEncodedField encodedField = appView.definitionFor(info.getField());
- if (encodedField == null) {
- assert false;
- return;
- }
- if (encodedField.isProgramField(appInfo)
- && info.isWritten()
- && predicate.test(encodedField)) {
- result.add(encodedField.field);
- }
- });
- result.removeAll(
- pinnedItems.stream()
- .filter(DexReference::isDexField)
- .map(DexReference::asDexField)
- .collect(Collectors.toSet()));
- return result;
- }
-
private static <T> SetWithReason<T> newSetWithoutReasonReporter() {
return new SetWithReason<>((f, r) -> {});
}
@@ -570,20 +541,29 @@
}
public boolean registerFieldRead(DexField field, DexEncodedMethod context) {
- return registerFieldAccess(field, context, true);
+ return registerFieldAccess(field, context, true, false);
+ }
+
+ public boolean registerReflectiveFieldRead(DexField field, DexEncodedMethod context) {
+ return registerFieldAccess(field, context, true, true);
}
public boolean registerFieldWrite(DexField field, DexEncodedMethod context) {
- return registerFieldAccess(field, context, false);
+ return registerFieldAccess(field, context, false, false);
}
- public boolean registerFieldAccess(DexField field, DexEncodedMethod context) {
- boolean changed = registerFieldAccess(field, context, true);
- changed |= registerFieldAccess(field, context, false);
+ public boolean registerReflectiveFieldWrite(DexField field, DexEncodedMethod context) {
+ return registerFieldAccess(field, context, false, true);
+ }
+
+ public boolean registerReflectiveFieldAccess(DexField field, DexEncodedMethod context) {
+ boolean changed = registerFieldAccess(field, context, true, true);
+ changed |= registerFieldAccess(field, context, false, true);
return changed;
}
- private boolean registerFieldAccess(DexField field, DexEncodedMethod context, boolean isRead) {
+ private boolean registerFieldAccess(
+ DexField field, DexEncodedMethod context, boolean isRead, boolean isReflective) {
FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field);
if (info == null) {
DexEncodedField encodedField = appInfo.resolveField(field);
@@ -612,6 +592,9 @@
} else if (info == MISSING_FIELD_ACCESS_INFO) {
return false;
}
+ if (isReflective) {
+ info.setHasReflectiveAccess();
+ }
return isRead ? info.recordRead(field, context) : info.recordWrite(field, context);
}
@@ -1740,8 +1723,8 @@
}
assert clazz.accessFlags.isInterface();
transitionReachableVirtualMethods(clazz, seen.newNestedScope());
- for (DexType subInterface : clazz.interfaces.values) {
- transitionDefaultMethodsForInstantiatedClass(subInterface, seen);
+ for (DexType superInterface : clazz.interfaces.values) {
+ transitionDefaultMethodsForInstantiatedClass(superInterface, seen);
}
}
@@ -2100,6 +2083,7 @@
return;
}
+ // TODO(mkroghj): Remove pinnedItems check here.
if (instantiatedTypes.contains(clazz)
|| instantiatedInterfaceTypes.contains(clazz)
|| pinnedItems.contains(clazz.type)) {
@@ -2118,8 +2102,8 @@
if (currentClass == null || currentClass.lookupVirtualMethod(possibleTarget) != null) {
continue;
}
- // TODO(zerny): Why does not not confer with lambdas and pinned too?
- if (instantiatedTypes.contains(currentClass)) {
+ if (instantiatedTypes.contains(currentClass)
+ || instantiatedInterfaceTypes.contains(currentClass)) {
markVirtualMethodAsLive(
clazz,
encodedPossibleTarget,
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index f27bd26..1cfd120 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -1281,9 +1281,6 @@
String message =
"Proguard configuration rule does not match anything: `" + rule.toString() + "`";
StringDiagnostic diagnostic = new StringDiagnostic(message, rule.getOrigin());
- if (!options.testing.allowUnusedProguardConfigurationRules) {
- throw options.reporter.fatalError(diagnostic);
- }
if (options.testing.reportUnusedProguardConfigurationRules) {
options.reporter.info(diagnostic);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 6ab0836..36a7030 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
@@ -897,10 +898,10 @@
for (DexEncodedMethod directMethod : source.directMethods()) {
if (directMethod.isInstanceInitializer()) {
- add(
- directMethods,
- renameConstructor(directMethod, availableMethodSignatures),
- MethodSignatureEquivalence.get());
+ DexEncodedMethod resultingConstructor =
+ renameConstructor(directMethod, availableMethodSignatures);
+ add(directMethods, resultingConstructor, MethodSignatureEquivalence.get());
+ blockRedirectionOfSuperCalls(resultingConstructor.method);
} else {
DexEncodedMethod resultingDirectMethod =
renameMethod(
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index c565c4e..cccaa07 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -896,9 +896,12 @@
return this;
}
- /**
- * Add Java-bytecode program data.
- */
+ /** Add Java-bytecode program data. */
+ public Builder addClassProgramData(byte[]... data) {
+ return addClassProgramData(Arrays.asList(data));
+ }
+
+ /** Add Java-bytecode program data. */
public Builder addClassProgramData(Collection<byte[]> data) {
for (byte[] datum : data) {
addClassProgramData(datum, Origin.unknown());
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 9eb8b6c..d0a4fb1 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -192,6 +192,7 @@
// Optimization-related flags. These should conform to -dontoptimize and disableAllOptimizations.
public boolean enableDynamicTypeOptimization = true;
+ public boolean enableFieldAssignmentTracker = true;
public boolean enableFieldBitAccessAnalysis =
System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
public boolean enableHorizontalClassMerging = true;
@@ -1005,7 +1006,6 @@
public boolean allowInvokeErrors = false;
public boolean disableL8AnnotationRemoval = false;
public boolean allowClassInlinerGracefulExit = true;
- public boolean allowUnusedProguardConfigurationRules = true;
public boolean reportUnusedProguardConfigurationRules = false;
public boolean alwaysUsePessimisticRegisterAllocation = false;
public boolean enableCheckCastAndInstanceOfRemoval = true;
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index 117ca7b..80b23ac 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -24,6 +24,16 @@
}
@Override
+ public String getStdout() {
+ return state.getStdout();
+ }
+
+ @Override
+ public String getStderr() {
+ return state.getStderr();
+ }
+
+ @Override
public D8TestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
return new D8TestRunResult(app, runtime, result);
}
diff --git a/src/test/java/com/android/tools/r8/DXTestCompileResult.java b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
index 3cf9b73..e362f78 100644
--- a/src/test/java/com/android/tools/r8/DXTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
public class DXTestCompileResult extends TestCompileResult<DXTestCompileResult, DXTestRunResult> {
@@ -23,6 +24,16 @@
}
@Override
+ public String getStdout() {
+ throw new Unimplemented("Unexpected attempt to access stdout from dx");
+ }
+
+ @Override
+ public String getStderr() {
+ throw new Unimplemented("Unexpected attempt to access stderr from dx");
+ }
+
+ @Override
public DXTestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
return new DXTestRunResult(app, runtime, result);
}
diff --git a/src/test/java/com/android/tools/r8/DumpInputsTest.java b/src/test/java/com/android/tools/r8/DumpInputsTest.java
index e7f2ef8..84febed 100644
--- a/src/test/java/com/android/tools/r8/DumpInputsTest.java
+++ b/src/test/java/com/android/tools/r8/DumpInputsTest.java
@@ -3,12 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils;
import java.io.IOException;
import java.nio.file.Files;
@@ -24,8 +24,6 @@
@RunWith(Parameterized.class)
public class DumpInputsTest extends TestBase {
- static final String EXPECTED = StringUtils.lines("Hello, world");
-
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
@@ -62,6 +60,9 @@
.addLibraryFiles(ToolHelper.getJava8RuntimeJar())
.addKeepMainRule(TestClass.class)
.addOptionsModification(options -> options.dumpInputToDirectory = dumpDir.toString())
+ .allowDiagnosticInfoMessages()
+ .compile()
+ .assertAllInfoMessagesMatch(containsString("Dumped compilation inputs to:"))
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines("Hello, world");
assertTrue(Files.isDirectory(dumpDir));
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
index 04a37fa..75cd5526 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
@@ -5,6 +5,7 @@
package com.android.tools.r8;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
@@ -58,6 +59,16 @@
}
@Override
+ public String getStdout() {
+ throw new Unimplemented("Unexpected attempt to access stdout from external R8");
+ }
+
+ @Override
+ public String getStderr() {
+ throw new Unimplemented("Unexpected attempt to access stderr from external R8");
+ }
+
+ @Override
public CodeInspector inspector() throws IOException, ExecutionException {
return new CodeInspector(app, proguardMap);
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index fbb8282..dbb3c6e 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
@@ -37,6 +38,16 @@
}
@Override
+ public String getStdout() {
+ throw new Unimplemented("Unexpected attempt to access stdout from Proguard");
+ }
+
+ @Override
+ public String getStderr() {
+ throw new Unimplemented("Unexpected attempt to access stderr from Proguard");
+ }
+
+ @Override
public CodeInspector inspector() throws IOException, ExecutionException {
return new CodeInspector(app, proguardMap);
}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 1dca05c..d80f7cd 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -3,9 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static org.hamcrest.CoreMatchers.containsString;
+
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.CollectingGraphConsumer;
@@ -26,14 +29,24 @@
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
+import org.hamcrest.core.IsAnything;
public abstract class R8TestBuilder<T extends R8TestBuilder<T>>
extends TestShrinkerBuilder<R8Command, Builder, R8TestCompileResult, R8TestRunResult, T> {
+ enum AllowedDiagnosticMessages {
+ ALL,
+ INFO,
+ NONE,
+ WARNING
+ }
+
R8TestBuilder(TestState state, Builder builder, Backend backend) {
super(state, builder, backend);
}
+ private AllowedDiagnosticMessages allowedDiagnosticMessages = AllowedDiagnosticMessages.NONE;
+ private boolean allowUnusedProguardConfigurationRules = false;
private boolean enableInliningAnnotations = false;
private boolean enableNeverClassInliningAnnotations = false;
private boolean enableMergeAnnotations = false;
@@ -105,14 +118,47 @@
builder.build(),
optionsConsumer.andThen(
options -> box.proguardConfiguration = options.getProguardConfiguration()));
- return new R8TestCompileResult(
- getState(),
- getOutputMode(),
- app.get(),
- box.proguardConfiguration,
- box.syntheticProguardRules,
- proguardMapBuilder.toString(),
- graphConsumer);
+ R8TestCompileResult compileResult =
+ new R8TestCompileResult(
+ getState(),
+ getOutputMode(),
+ app.get(),
+ box.proguardConfiguration,
+ box.syntheticProguardRules,
+ proguardMapBuilder.toString(),
+ graphConsumer);
+ switch (allowedDiagnosticMessages) {
+ case ALL:
+ compileResult.assertDiagnosticMessageThatMatches(new IsAnything<>());
+ break;
+ case INFO:
+ compileResult.assertOnlyInfos();
+ break;
+ case NONE:
+ if (allowUnusedProguardConfigurationRules) {
+ compileResult
+ .assertAllInfoMessagesMatch(
+ containsString("Proguard configuration rule does not match anything"))
+ .assertNoErrorMessages()
+ .assertNoWarningMessages();
+ } else {
+ compileResult.assertNoMessages();
+ }
+ break;
+ case WARNING:
+ compileResult.assertOnlyWarnings();
+ break;
+ default:
+ throw new Unreachable();
+ }
+ if (allowUnusedProguardConfigurationRules) {
+ compileResult.assertInfoMessageThatMatches(
+ containsString("Proguard configuration rule does not match anything"));
+ } else {
+ compileResult.assertNoInfoMessageThatMatches(
+ containsString("Proguard configuration rule does not match anything"));
+ }
+ return compileResult;
}
public Builder getBuilder() {
@@ -201,9 +247,45 @@
return addOptionsModification(options -> options.testing.allowClassInlinerGracefulExit = true);
}
+ public T allowDiagnosticMessages() {
+ assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
+ allowedDiagnosticMessages = AllowedDiagnosticMessages.ALL;
+ return self();
+ }
+
+ public T allowDiagnosticInfoMessages() {
+ return allowDiagnosticInfoMessages(true);
+ }
+
+ public T allowDiagnosticInfoMessages(boolean condition) {
+ if (condition) {
+ assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
+ allowedDiagnosticMessages = AllowedDiagnosticMessages.INFO;
+ }
+ return self();
+ }
+
+ public T allowDiagnosticWarningMessages() {
+ return allowDiagnosticWarningMessages(true);
+ }
+
+ public T allowDiagnosticWarningMessages(boolean condition) {
+ if (condition) {
+ assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
+ allowedDiagnosticMessages = AllowedDiagnosticMessages.WARNING;
+ }
+ return self();
+ }
+
public T allowUnusedProguardConfigurationRules() {
- return addOptionsModification(
- options -> options.testing.allowUnusedProguardConfigurationRules = true);
+ return allowUnusedProguardConfigurationRules(true);
+ }
+
+ public T allowUnusedProguardConfigurationRules(boolean condition) {
+ if (condition) {
+ allowUnusedProguardConfigurationRules = true;
+ }
+ return self();
}
public T enableAlwaysInliningAnnotations() {
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index c4ec2aa..ae6fc69 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -53,6 +53,16 @@
}
@Override
+ public String getStdout() {
+ return state.getStdout();
+ }
+
+ @Override
+ public String getStderr() {
+ return state.getStderr();
+ }
+
+ @Override
public CodeInspector inspector() throws IOException, ExecutionException {
return new CodeInspector(app, proguardMap);
}
diff --git a/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java b/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java
index 8d6b8d2..06cba56 100644
--- a/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java
+++ b/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java
@@ -4,7 +4,9 @@
package com.android.tools.r8;
+import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -57,8 +59,16 @@
: containsString("AbstractMethodError");
testForR8(parameters.getBackend())
.addProgramClasses(Main.class)
+ .allowDiagnosticWarningMessages(
+ parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.O))
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
+ .compile()
+ .assertAllWarningMessagesMatch(
+ anyOf(
+ equalTo(
+ "Lambda expression implements missing interface `java.util.function.Supplier`"),
+ containsString("required for default or static interface methods desugaring")))
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatMatches(expectedError);
}
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index e04a2c1..87cc650 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -5,7 +5,10 @@
import static com.android.tools.r8.TestBase.Backend.DEX;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.TestBase.Backend;
@@ -78,6 +81,15 @@
public abstract TestDiagnosticMessages getDiagnosticMessages();
+ public CR inspectDiagnosticMessages(Consumer<TestDiagnosticMessages> consumer) {
+ consumer.accept(getDiagnosticMessages());
+ return self();
+ }
+
+ public abstract String getStdout();
+
+ public abstract String getStderr();
+
public OutputMode getOutputMode() {
return outputMode;
}
@@ -268,21 +280,55 @@
return self();
}
+ public CR assertDiagnosticMessageThatMatches(Matcher<String> matcher) {
+ getDiagnosticMessages().assertDiagnosticMessageThatMatches(matcher);
+ return self();
+ }
+
public CR assertInfoMessageThatMatches(Matcher<String> matcher) {
getDiagnosticMessages().assertInfoMessageThatMatches(matcher);
return self();
}
+ public CR assertAllInfoMessagesMatch(Matcher<String> matcher) {
+ return assertNoInfoMessageThatMatches(not(matcher));
+ }
+
public CR assertNoInfoMessageThatMatches(Matcher<String> matcher) {
getDiagnosticMessages().assertNoInfoMessageThatMatches(matcher);
return self();
}
+ public CR assertAtLeastOneInfoMessage() {
+ assertTrue(getDiagnosticMessages().getInfos().size() >= 1);
+ return self();
+ }
+
+ public CR assertInfosCount(int count) {
+ getDiagnosticMessages().assertInfosCount(count);
+ return self();
+ }
+
+ public CR assertNoInfoMessages() {
+ getDiagnosticMessages().assertInfosCount(0);
+ return self();
+ }
+
public CR assertWarningMessageThatMatches(Matcher<String> matcher) {
getDiagnosticMessages().assertWarningMessageThatMatches(matcher);
return self();
}
+ public CR assertAllWarningMessagesMatch(Matcher<String> matcher) {
+ getDiagnosticMessages().assertNoWarningMessageThatMatches(not(matcher));
+ return self();
+ }
+
+ public CR assertNoWarningMessages() {
+ getDiagnosticMessages().assertWarningsCount(0);
+ return self();
+ }
+
public CR assertNoWarningMessageThatMatches(Matcher<String> matcher) {
getDiagnosticMessages().assertNoWarningMessageThatMatches(matcher);
return self();
@@ -293,11 +339,36 @@
return self();
}
+ public CR assertNoErrorMessages() {
+ getDiagnosticMessages().assertErrorsCount(0);
+ return self();
+ }
+
public CR assertNoErrorMessageThatMatches(Matcher<String> matcher) {
getDiagnosticMessages().assertNoErrorMessageThatMatches(matcher);
return self();
}
+ public CR assertNoStdout() {
+ assertEquals("", getStdout());
+ return self();
+ }
+
+ public CR assertStdoutThatMatches(Matcher<String> matcher) {
+ assertThat(getStdout(), matcher);
+ return self();
+ }
+
+ public CR assertNoStderr() {
+ assertEquals("", getStderr());
+ return self();
+ }
+
+ public CR assertStderrThatMatches(Matcher<String> matcher) {
+ assertThat(getStderr(), matcher);
+ return self();
+ }
+
public CR disassemble(PrintStream ps) throws IOException, ExecutionException {
ToolHelper.disassemble(app, ps);
return self();
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 8c6e7c2..628e9c5 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -10,8 +10,10 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.ForwardingOutputStream;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.base.Suppliers;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Path;
@@ -35,20 +37,22 @@
public static final Consumer<InternalOptions> DEFAULT_OPTIONS =
options -> {
options.testing.allowClassInlinerGracefulExit = false;
- options.testing.allowUnusedProguardConfigurationRules = false;
options.testing.reportUnusedProguardConfigurationRules = true;
};
final Backend backend;
// Default initialized setup. Can be overwritten if needed.
+ private boolean allowStdoutMessages = false;
+ private boolean allowStderrMessages = false;
private boolean useDefaultRuntimeLibrary = true;
private final List<Path> additionalRunClassPath = new ArrayList<>();
private ProgramConsumer programConsumer;
private StringConsumer mainDexListConsumer;
private AndroidApiLevel defaultMinApiLevel = ToolHelper.getMinApiLevelForDexVm();
private Consumer<InternalOptions> optionsConsumer = DEFAULT_OPTIONS;
- private PrintStream stdout = null;
+ private ByteArrayOutputStream stdout = null;
+ private ByteArrayOutputStream stderr = null;
protected OutputMode outputMode = OutputMode.DexIndexed;
TestCompilerBuilder(TestState state, B builder, Backend backend) {
@@ -92,16 +96,32 @@
}
}
PrintStream oldOut = System.out;
+ PrintStream oldErr = System.err;
+ CR cr = null;
try {
if (stdout != null) {
- System.setOut(stdout);
+ System.setOut(new PrintStream(new ForwardingOutputStream(stdout, System.out)));
}
- CR cr = internalCompile(builder, optionsConsumer, Suppliers.memoize(sink::build));
+ if (stderr != null) {
+ System.setErr(new PrintStream(new ForwardingOutputStream(stderr, System.err)));
+ }
+ cr = internalCompile(builder, optionsConsumer, Suppliers.memoize(sink::build));
cr.addRunClasspathFiles(additionalRunClassPath);
return cr;
} finally {
if (stdout != null) {
+ getState().setStdout(stdout.toString());
System.setOut(oldOut);
+ if (cr != null && !allowStdoutMessages) {
+ cr.assertNoStdout();
+ }
+ }
+ if (stderr != null) {
+ getState().setStderr(stderr.toString());
+ System.setErr(oldErr);
+ if (cr != null && !allowStderrMessages) {
+ cr.assertNoStderr();
+ }
}
}
}
@@ -288,12 +308,28 @@
return self();
}
- public T redirectStdOut(PrintStream printStream) {
- assert stdout == null;
- stdout = printStream;
+ public T allowStdoutMessages() {
+ allowStdoutMessages = true;
return self();
}
+ public T collectStdout() {
+ assert stdout == null;
+ stdout = new ByteArrayOutputStream();
+ return allowStdoutMessages();
+ }
+
+ public T allowStderrMessages() {
+ allowStdoutMessages = true;
+ return self();
+ }
+
+ public T collectStderr() {
+ assert stderr == null;
+ stderr = new ByteArrayOutputStream();
+ return allowStderrMessages();
+ }
+
public T enableCoreLibraryDesugaring(AndroidApiLevel minAPILevel) {
return enableCoreLibraryDesugaring(minAPILevel, null);
}
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index 702815a..0dd7e67 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -9,35 +9,41 @@
public interface TestDiagnosticMessages {
- public List<Diagnostic> getInfos();
+ List<Diagnostic> getInfos();
- public List<Diagnostic> getWarnings();
+ List<Diagnostic> getWarnings();
- public List<Diagnostic> getErrors();
+ List<Diagnostic> getErrors();
- public TestDiagnosticMessages assertNoMessages();
+ TestDiagnosticMessages assertNoMessages();
- public TestDiagnosticMessages assertOnlyInfos();
+ TestDiagnosticMessages assertOnlyInfos();
- public TestDiagnosticMessages assertOnlyWarnings();
+ TestDiagnosticMessages assertOnlyWarnings();
- public TestDiagnosticMessages assertOnlyErrors();
+ TestDiagnosticMessages assertOnlyErrors();
- public TestDiagnosticMessages assertInfosCount(int count);
+ TestDiagnosticMessages assertInfosCount(int count);
- public TestDiagnosticMessages assertWarningsCount(int count);
+ TestDiagnosticMessages assertWarningsCount(int count);
- public TestDiagnosticMessages assertErrorsCount(int count);
+ TestDiagnosticMessages assertErrorsCount(int count);
- public TestDiagnosticMessages assertInfoMessageThatMatches(Matcher<String> matcher);
+ TestDiagnosticMessages assertDiagnosticMessageThatMatches(Matcher<String> matcher);
- public TestDiagnosticMessages assertNoInfoMessageThatMatches(Matcher<String> matcher);
+ TestDiagnosticMessages assertInfoMessageThatMatches(Matcher<String> matcher);
- public TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher);
+ TestDiagnosticMessages assertAllInfoMessagesMatch(Matcher<String> matcher);
- public TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher);
+ TestDiagnosticMessages assertNoInfoMessageThatMatches(Matcher<String> matcher);
- public TestDiagnosticMessages assertErrorMessageThatMatches(Matcher<String> matcher);
+ TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher);
- public TestDiagnosticMessages assertNoErrorMessageThatMatches(Matcher<String> matcher);
+ TestDiagnosticMessages assertAllWarningMessagesMatch(Matcher<String> matcher);
+
+ TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher);
+
+ TestDiagnosticMessages assertErrorMessageThatMatches(Matcher<String> matcher);
+
+ TestDiagnosticMessages assertNoErrorMessageThatMatches(Matcher<String> matcher);
}
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index 478a60d..44f046b 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -4,11 +4,13 @@
package com.android.tools.r8;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.List;
import org.hamcrest.Matcher;
@@ -122,22 +124,24 @@
}
private TestDiagnosticMessages assertMessageThatMatches(
- List<Diagnostic> diagnostics, String tag, Matcher<String> matcher) {
- assertNotEquals(0, diagnostics.size());
- for (int i = 0; i < diagnostics.size(); i++) {
- if (matcher.matches(diagnostics.get(i).getDiagnosticMessage())) {
+ Iterable<Diagnostic> diagnostics, String tag, Matcher<String> matcher) {
+ int numberOfDiagnostics = 0;
+ for (Diagnostic diagnostic : diagnostics) {
+ if (matcher.matches(diagnostic.getDiagnosticMessage())) {
return this;
}
+ numberOfDiagnostics++;
}
+ assertNotEquals(0, numberOfDiagnostics);
StringBuilder builder = new StringBuilder("No " + tag + " matches " + matcher.toString());
builder.append(System.lineSeparator());
if (getWarnings().size() == 0) {
builder.append("There were no " + tag + "s.");
} else {
- builder.append("There were " + diagnostics.size() + " "+ tag + "s:");
+ builder.append("There were " + numberOfDiagnostics + " " + tag + "s:");
builder.append(System.lineSeparator());
- for (int i = 0; i < diagnostics.size(); i++) {
- builder.append(diagnostics.get(i).getDiagnosticMessage());
+ for (Diagnostic diagnostic : diagnostics) {
+ builder.append(diagnostic.getDiagnosticMessage());
builder.append(System.lineSeparator());
}
}
@@ -156,18 +160,38 @@
return this;
}
+ @Override
+ public TestDiagnosticMessages assertDiagnosticMessageThatMatches(Matcher<String> matcher) {
+ return assertMessageThatMatches(
+ Iterables.concat(getInfos(), getWarnings(), getErrors()), "diagnostic message", matcher);
+ }
+
+ @Override
public TestDiagnosticMessages assertInfoMessageThatMatches(Matcher<String> matcher) {
return assertMessageThatMatches(getInfos(), "info", matcher);
}
+ @Override
+ public TestDiagnosticMessages assertAllInfoMessagesMatch(Matcher<String> matcher) {
+ return assertNoInfoMessageThatMatches(not(matcher));
+ }
+
+ @Override
public TestDiagnosticMessages assertNoInfoMessageThatMatches(Matcher<String> matcher) {
return assertNoMessageThatMatches(getInfos(), "info", matcher);
}
+ @Override
public TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher) {
return assertMessageThatMatches(getWarnings(), "warning", matcher);
}
+ @Override
+ public TestDiagnosticMessages assertAllWarningMessagesMatch(Matcher<String> matcher) {
+ return assertNoWarningMessageThatMatches(not(matcher));
+ }
+
+ @Override
public TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher) {
return assertNoMessageThatMatches(getWarnings(), "warning", matcher);
}
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 35d37cc..b29d851 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -160,6 +160,10 @@
return withApiFilter(api -> true);
}
+ public TestParametersBuilder withApiLevel(AndroidApiLevel api) {
+ return withApiFilter(api::equals);
+ }
+
public TestParametersBuilder withApiLevelsStartingAtIncluding(AndroidApiLevel startInclusive) {
return withApiFilter(api -> startInclusive.getLevel() <= api.getLevel());
}
diff --git a/src/test/java/com/android/tools/r8/TestState.java b/src/test/java/com/android/tools/r8/TestState.java
index bdb4e2f..78388bc 100644
--- a/src/test/java/com/android/tools/r8/TestState.java
+++ b/src/test/java/com/android/tools/r8/TestState.java
@@ -12,6 +12,9 @@
private final TemporaryFolder temp;
private final TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl();
+ private String stdout;
+ private String stderr;
+
public TestState(TemporaryFolder temp) {
this.temp = temp;
}
@@ -27,4 +30,20 @@
public TestDiagnosticMessages getDiagnosticsMessages() {
return messages;
}
+
+ public String getStdout() {
+ return stdout;
+ }
+
+ void setStdout(String stdout) {
+ this.stdout = stdout;
+ }
+
+ public String getStderr() {
+ return stderr;
+ }
+
+ void setStderr(String stderr) {
+ this.stderr = stderr;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index ddb0608..1bde24a 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static com.google.common.io.ByteStreams.toByteArray;
+import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -103,7 +104,14 @@
.setMode(mode)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
+ .allowDiagnosticInfoMessages(mode == CompilationMode.DEBUG)
.compile()
+ .assertAllInfoMessagesMatch(
+ anyOf(
+ containsString("Stripped invalid locals information from 1 method."),
+ containsString("Methods with invalid locals information:"),
+ containsString(
+ "Some warnings are typically a sign of using an outdated Java toolchain.")))
.apply(c -> FileUtils.writeTextFile(map, c.getProguardMap()))
.writeToZip(jar);
}
diff --git a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
index 5cbb40d..363008d 100644
--- a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
+++ b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
@@ -180,13 +180,12 @@
.addProgramClassFileData(junitClasses)
.addKeepAllClassesRule()
.addOptionsModification(options -> options.lookupLibraryBeforeProgram = false)
- .compileWithExpectedDiagnostics(
+ .allowDiagnosticWarningMessages(libraryContainsJUnit())
+ .compile()
+ .inspectDiagnosticMessages(
diagnostics -> {
if (libraryContainsJUnit()) {
- diagnostics.assertOnlyWarnings();
checkDiagnostics(diagnostics.getWarnings());
- } else {
- diagnostics.assertNoMessages();
}
})
.inspect(this::testCaseClassInResult);
diff --git a/src/test/java/com/android/tools/r8/classmerging/B141942381.java b/src/test/java/com/android/tools/r8/classmerging/B141942381.java
index 2c47534..36f320b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/B141942381.java
+++ b/src/test/java/com/android/tools/r8/classmerging/B141942381.java
@@ -28,7 +28,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public B141942381(TestParameters parameters) {
@@ -49,7 +49,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(B141942381.class)
.addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.addKeepAttributes("Signatures")
.enableNeverClassInliningAnnotations()
.noMinification()
diff --git a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
index 09489c9..6ccae1f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.StringConsumer.FileConsumer;
import com.android.tools.r8.TestBase;
@@ -140,14 +141,17 @@
"classmerging.ArrayTypeCollisionTest$A",
"classmerging.ArrayTypeCollisionTest$B");
runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(
+ getProguardConfig(
+ EXAMPLE_KEEP,
+ "-neverinline public class classmerging.ArrayTypeCollisionTest {",
+ " static void method(...);",
+ "}"))
+ .allowUnusedProguardConfigurationRules(),
main,
programFiles,
- preservedClassNames::contains,
- getProguardConfig(
- EXAMPLE_KEEP,
- "-neverinline public class classmerging.ArrayTypeCollisionTest {",
- " static void method(...);",
- "}"));
+ preservedClassNames::contains);
}
/**
@@ -199,17 +203,18 @@
// Run test.
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(
+ "-keep class " + main + " {",
+ " public static void main(...);",
+ "}",
+ "-neverinline class " + main + " {",
+ " static classmerging.A[] method(...);",
+ " static classmerging.B[] method(...);",
+ "}"),
main,
jasminBuilder.build(),
- preservedClassNames::contains,
- StringUtils.joinLines(
- "-keep class " + main + " {",
- " public static void main(...);",
- "}",
- "-neverinline class " + main + " {",
- " static classmerging.A[] method(...);",
- " static classmerging.B[] method(...);",
- "}"));
+ preservedClassNames::contains);
}
// This test has a cycle in the call graph consisting of the methods A.<init> and B.<init>.
@@ -231,7 +236,13 @@
Set<String> preservedClassNames =
ImmutableSet.of("classmerging.CallGraphCycleTest", "classmerging.CallGraphCycleTest$B");
for (int i = 0; i < 5; i++) {
- runTest(main, programFiles, preservedClassNames::contains);
+ runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
}
}
@@ -250,10 +261,12 @@
"classmerging.ConflictInGeneratedNameTest$B");
CodeInspector inspector =
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
preservedClassNames::contains,
- getProguardConfig(EXAMPLE_KEEP),
options -> {
configure(options);
// Avoid that direct methods in B get inlined.
@@ -321,7 +334,13 @@
ImmutableSet.of(
"classmerging.FieldCollisionTest",
"classmerging.FieldCollisionTest$B");
- runTest(main, programFiles, preservedClassNames::contains);
+ runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
}
@Test
@@ -342,10 +361,12 @@
"classmerging.LambdaRewritingTest$FunctionImpl",
"classmerging.LambdaRewritingTest$InterfaceImpl");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
name -> preservedClassNames.contains(name) || name.contains("$Lambda$"),
- getProguardConfig(JAVA8_EXAMPLE_KEEP),
options -> {
this.configure(options);
options.enableClassInlining = false;
@@ -367,10 +388,12 @@
"classmerging.ConflictingInterfaceSignaturesTest",
"classmerging.ConflictingInterfaceSignaturesTest$InterfaceImpl");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
preservedClassNames::contains,
- getProguardConfig(EXAMPLE_KEEP),
options -> {
this.configure(options);
options.enableInlining = false;
@@ -399,7 +422,13 @@
"classmerging.MethodCollisionTest$B",
"classmerging.MethodCollisionTest$C",
"classmerging.MethodCollisionTest$D");
- runTest(main, programFiles, preservedClassNames::contains);
+ runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
}
@Test
@@ -418,7 +447,12 @@
"classmerging.NestedDefaultInterfaceMethodsTest$B",
"classmerging.NestedDefaultInterfaceMethodsTest$C");
runTest(
- main, programFiles, preservedClassNames::contains, getProguardConfig(JAVA8_EXAMPLE_KEEP));
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
}
@Test
@@ -436,14 +470,16 @@
"classmerging.NestedDefaultInterfaceMethodsTest$B",
"classmerging.NestedDefaultInterfaceMethodsTest$C");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
main,
AndroidApp.builder()
.addProgramFiles(programFiles)
.addClassProgramData(
NestedDefaultInterfaceMethodsTestDump.CDump.dump(), Origin.unknown())
.build(),
- preservedClassNames::contains,
- getProguardConfig(JAVA8_EXAMPLE_KEEP));
+ preservedClassNames::contains);
}
@Test
@@ -463,10 +499,12 @@
"classmerging.PinnedParameterTypesTest$InterfaceImpl",
"classmerging.PinnedParameterTypesTest$TestClass");
runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"))
+ .allowUnusedProguardConfigurationRules(),
main,
programFiles,
- preservedClassNames::contains,
- getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"));
+ preservedClassNames::contains);
}
@Test
@@ -486,10 +524,12 @@
"classmerging.PinnedArrayParameterTypesTest$InterfaceImpl",
"classmerging.PinnedArrayParameterTypesTest$TestClass");
runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"))
+ .allowUnusedProguardConfigurationRules(),
main,
programFiles,
- preservedClassNames::contains,
- getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"));
+ preservedClassNames::contains);
}
@Test
@@ -515,10 +555,12 @@
" 1:1:void <init>():20:20 -> <init>",
" 1:1:void test():23:23 -> test");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
Predicates.alwaysTrue(),
- getProguardConfig(EXAMPLE_KEEP),
options -> {
configure(options);
options.enableVerticalClassMerging = false;
@@ -546,10 +588,12 @@
Set<String> preservedClassNames =
ImmutableSet.of("classmerging.ProguardFieldMapTest", "classmerging.ProguardFieldMapTest$B");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
preservedClassNames::contains,
- getProguardConfig(EXAMPLE_KEEP),
options -> {
configure(options);
options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
@@ -583,10 +627,12 @@
" 1:1:void <init>():22:22 -> <init>",
" 1:2:void method():26:27 -> method");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
Predicates.alwaysTrue(),
- getProguardConfig(EXAMPLE_KEEP),
options -> {
configure(options);
options.enableVerticalClassMerging = false;
@@ -617,10 +663,12 @@
ImmutableSet.of(
"classmerging.ProguardMethodMapTest", "classmerging.ProguardMethodMapTest$B");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
preservedClassNames::contains,
- getProguardConfig(EXAMPLE_KEEP),
options -> {
configure(options);
options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
@@ -655,12 +703,16 @@
" 2:2:void classmerging.ProguardMethodMapTest$A.method():17:17 -> method",
" 2:2:void method():27 -> method");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(
+ getProguardConfig(
+ EXAMPLE_KEEP,
+ "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
+ + " method(); }"))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
Predicates.alwaysTrue(),
- getProguardConfig(
- EXAMPLE_KEEP,
- "-forceinline class classmerging.ProguardMethodMapTest$A { public void method(); }"),
options -> {
configure(options);
options.enableVerticalClassMerging = false;
@@ -688,12 +740,16 @@
ImmutableSet.of(
"classmerging.ProguardMethodMapTest", "classmerging.ProguardMethodMapTest$B");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(
+ getProguardConfig(
+ EXAMPLE_KEEP,
+ "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
+ + " method(); }"))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
preservedClassNames::contains,
- getProguardConfig(
- EXAMPLE_KEEP,
- "-forceinline class classmerging.ProguardMethodMapTest$A { public void method(); }"),
options -> {
configure(options);
options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
@@ -717,7 +773,13 @@
ImmutableSet.of(
"classmerging.SubClassThatReferencesSuperMethod",
"classmerging.SuperCallRewritingTest");
- runTest(main, programFiles, preservedClassNames::contains);
+ runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
}
// When a subclass A has been merged into its subclass B, we rewrite invoke-super calls that hit
@@ -924,10 +986,11 @@
// Run test.
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(String.format("-keep class %s { public static void main(...); }", main)),
main,
appBuilder.build(),
- preservedClassNames::contains,
- String.format("-keep class %s { public static void main(...); }", main));
+ preservedClassNames::contains);
}
@Test
@@ -953,10 +1016,12 @@
"classmerging.SyntheticBridgeSignaturesTest$ASub",
"classmerging.SyntheticBridgeSignaturesTest$BSub");
runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
preservedClassNames::contains,
- getProguardConfig(EXAMPLE_KEEP),
options -> {
this.configure(options);
if (!allowInlining) {
@@ -989,7 +1054,13 @@
ImmutableSet.of(
"classmerging.ConflictingInterfaceSignaturesTest",
"classmerging.ConflictingInterfaceSignaturesTest$InterfaceImpl");
- runTest(main, programFiles, preservedClassNames::contains);
+ runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
}
// If an exception class A is merged into another exception class B, then all exception tables
@@ -1010,7 +1081,14 @@
"classmerging.ExceptionTest",
"classmerging.ExceptionTest$ExceptionB",
"classmerging.ExceptionTest$Exception2");
- CodeInspector inspector = runTest(main, programFiles, preservedClassNames::contains);
+ CodeInspector inspector =
+ runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
ClassSubject mainClass = inspector.clazz(main);
assertThat(mainClass, isPresent());
@@ -1061,7 +1139,13 @@
method.getMethod().getCode().asCfCode().toString(),
containsString("invokeinterface classmerging.MergeDefaultMethodIntoClassTest$A.f()V"));
- runTestOnInput(main, app, preservedClassNames::contains, getProguardConfig(JAVA8_EXAMPLE_KEEP));
+ runTestOnInput(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ app,
+ preservedClassNames::contains);
}
@Test
@@ -1079,7 +1163,13 @@
"classmerging.ClassWithNativeMethodTest",
"classmerging.ClassWithNativeMethodTest$A",
"classmerging.ClassWithNativeMethodTest$B");
- runTest(main, programFiles, preservedClassNames::contains);
+ runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
}
@Test
@@ -1104,7 +1194,13 @@
"classmerging.SimpleInterfaceAccessTest$OtherSimpleInterfaceImpl",
"classmerging.pkg.SimpleInterfaceImplRetriever",
"classmerging.pkg.SimpleInterfaceImplRetriever$SimpleInterfaceImpl");
- runTest(main, programFiles, preservedClassNames::contains);
+ runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
}
@Test
@@ -1130,13 +1226,16 @@
// Allow access modifications (and prevent SimpleInterfaceImplRetriever from being removed as
// a result of inlining).
runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(
+ getProguardConfig(
+ EXAMPLE_KEEP,
+ "-allowaccessmodification",
+ "-keep public class classmerging.pkg.SimpleInterfaceImplRetriever"))
+ .allowUnusedProguardConfigurationRules(),
main,
programFiles,
- preservedClassNames::contains,
- getProguardConfig(
- EXAMPLE_KEEP,
- "-allowaccessmodification",
- "-keep public class classmerging.pkg.SimpleInterfaceImplRetriever"));
+ preservedClassNames::contains);
}
// TODO(christofferqa): This test checks that the invoke-super instruction in B is not rewritten
@@ -1158,11 +1257,14 @@
"classmerging.RewritePinnedMethodTest$A",
"classmerging.RewritePinnedMethodTest$C");
runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(
+ getProguardConfig(
+ EXAMPLE_KEEP, "-keep class classmerging.RewritePinnedMethodTest$A { *; }"))
+ .allowUnusedProguardConfigurationRules(),
main,
programFiles,
- preservedClassNames::contains,
- getProguardConfig(
- EXAMPLE_KEEP, "-keep class classmerging.RewritePinnedMethodTest$A { *; }"));
+ preservedClassNames::contains);
}
@Test
@@ -1177,57 +1279,60 @@
Set<String> preservedClassNames =
ImmutableSet.of(
"classmerging.TemplateMethodTest", "classmerging.TemplateMethodTest$AbstractClassImpl");
- runTest(main, programFiles, preservedClassNames::contains);
+ runTest(
+ testForR8(Backend.DEX)
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains);
}
private CodeInspector runTest(
- String main, Path[] programFiles, Predicate<String> preservedClassNames) throws Throwable {
- return runTest(main, programFiles, preservedClassNames, getProguardConfig(EXAMPLE_KEEP));
- }
-
- private CodeInspector runTest(
+ R8FullTestBuilder builder,
String main,
Path[] programFiles,
- Predicate<String> preservedClassNames,
- String proguardConfig)
+ Predicate<String> preservedClassNames)
throws Throwable {
- return runTestOnInput(
- main, readProgramFiles(programFiles), preservedClassNames, proguardConfig);
+ return runTestOnInput(builder, main, readProgramFiles(programFiles), preservedClassNames);
}
private CodeInspector runTestOnInput(
- String main, AndroidApp input, Predicate<String> preservedClassNames, String proguardConfig)
+ R8FullTestBuilder builder,
+ String main,
+ AndroidApp input,
+ Predicate<String> preservedClassNames)
throws Throwable {
- return runTestOnInput(main, input, preservedClassNames, proguardConfig, this::configure);
+ return runTestOnInput(builder, main, input, preservedClassNames, this::configure);
}
private CodeInspector runTestOnInput(
+ R8FullTestBuilder builder,
String main,
AndroidApp input,
Predicate<String> preservedClassNames,
- String proguardConfig,
Consumer<InternalOptions> optionsConsumer)
throws Throwable {
return runTestOnInput(
+ builder,
main,
input,
preservedClassNames,
- proguardConfig,
optionsConsumer,
new VerticalClassMergerDebugTest(main));
}
private CodeInspector runTestOnInput(
+ R8FullTestBuilder builder,
String main,
AndroidApp input,
Predicate<String> preservedClassNames,
- String proguardConfig,
Consumer<InternalOptions> optionsConsumer,
VerticalClassMergerDebugTest debugTestRunner)
throws Throwable {
Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
R8TestCompileResult compileResult =
- testForR8(Backend.DEX)
+ builder
.apply(
b -> {
// Some tests add DEX inputs, so circumvent the check by adding directly to the
@@ -1238,12 +1343,10 @@
}
})
.noMinification()
- .addKeepRules(proguardConfig)
.enableProguardTestOptions()
.addOptionsModification(
o -> {
optionsConsumer.accept(o);
- o.testing.allowUnusedProguardConfigurationRules = true;
o.proguardMapConsumer = new FileConsumer(proguardMapPath, o.proguardMapConsumer);
})
.compile();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
index edaf321..8334f1d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
@@ -62,6 +62,25 @@
StringUtils.lines("false", "false", "false", "false", "false", "false"));
}
+ @Test
+ public void testCustomCollectionR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(CustomCollectionForwardingTest.class)
+ .addKeepMainRule(Executor.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(
+ StringUtils.lines("false", "false", "false", "false", "false", "false"));
+ }
+
private void assertForwardingMethods(CodeInspector inspector) {
if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
return;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 61296c9..628fbfe 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -16,7 +16,9 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -30,6 +32,26 @@
public class DesugaredLibraryTestBase extends TestBase {
+ // For conversions tests, we need DexRuntimes where classes to convert are present (DexRuntimes
+ // above N and O depending if Stream or Time APIs are used), but we need to compile the program
+ // with a minAPI below to force the use of conversions.
+ protected static TestParametersCollection getConversionParametersUpToExcluding(
+ AndroidApiLevel apiLevel) {
+ if (apiLevel == AndroidApiLevel.N) {
+ return getTestParameters()
+ .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+ .withApiLevelsEndingAtExcluding(AndroidApiLevel.N)
+ .build();
+ }
+ if (apiLevel == AndroidApiLevel.O) {
+ return getTestParameters()
+ .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+ .withApiLevelsEndingAtExcluding(AndroidApiLevel.O)
+ .build();
+ }
+ throw new Error("Unsupported conversion parameters");
+ }
+
protected boolean requiresEmulatedInterfaceCoreLibDesugaring(TestParameters parameters) {
return parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
index 7ca5301..e345069 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
@@ -80,10 +80,7 @@
@Test
public void testJavaUtilFunctionR8() throws Exception {
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
- // TODO(b/139398549): Enable test on API 26+.
- Assume.assumeTrue(parameters.getApiLevel().getLevel() < 26);
testForR8(parameters.getBackend())
- // Following two for checkRewrittenArguments.
.enableInliningAnnotations()
.noMinification()
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
index 1cb8f74..579b6f6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
@@ -7,15 +7,16 @@
import static org.hamcrest.core.StringContains.containsString;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
+import java.util.List;
import java.util.Random;
import java.util.function.IntUnaryOperator;
import java.util.stream.IntStream;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -25,21 +26,27 @@
public class APIConversionTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters()
- .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
- .withApiLevelsEndingAtExcluding(AndroidApiLevel.M)
- .build();
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "[5, 6, 7]", "$r8$wrapper$java$util$stream$IntStream$-V-WRP", "IntSummaryStatistics");
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
}
- public APIConversionTest(TestParameters parameters) {
+ public APIConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
@Test
public void testAPIConversionNoDesugaring() throws Exception {
+ Assume.assumeTrue("No need to test twice", shrinkDesugaredLibrary);
testForD8()
.addInnerClasses(APIConversionTest.class)
.setMinApi(parameters.getApiLevel())
@@ -54,20 +61,41 @@
}
@Test
- public void testAPIConversionDesugaring() throws Exception {
+ public void testAPIConversionDesugaringD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addInnerClasses(APIConversionTest.class)
.setMinApi(parameters.getApiLevel())
- .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.assertNoMessages()
- .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Executor.class)
- .assertSuccessWithOutput(
- StringUtils.lines(
- "[5, 6, 7]",
- "$r8$wrapper$java$util$stream$IntStream$-V-WRP",
- "IntSummaryStatistics"));
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testAPIConversionDesugaringR8() throws Exception {
+ Assume.assumeTrue("Invalid runtime library (missing applyAsInt)", false);
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(APIConversionTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Executor.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .assertNoMessages()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
}
static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
index ef30ad1..75d46fe 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
@@ -4,40 +4,99 @@
package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
+import java.util.List;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
+import org.junit.BeforeClass;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class AllOptionalConversionTest extends DesugaredLibraryTestBase {
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "Optional[value]",
+ "OptionalDouble[1.0]",
+ "OptionalInt[1]",
+ "OptionalLong[1]",
+ "Optional[value]",
+ "value");
+
+ private static Path CUSTOM_LIB;
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+ }
+
+ public AllOptionalConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void compileCustomLib() throws Exception {
+ CUSTOM_LIB =
+ testForD8(getStaticTemp())
+ .addProgramClasses(CustomLibClass.class)
+ .setMinApi(MIN_SUPPORTED)
+ .compile()
+ .writeToZip();
+ }
+
@Test
- public void testRewrittenAPICalls() throws Exception {
- Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+ public void testRewrittenAPICallsD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
- .setMinApi(AndroidApiLevel.B)
+ .setMinApi(parameters.getApiLevel())
.addProgramClasses(Executor.class)
.addLibraryClasses(CustomLibClass.class)
- .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
- .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
- .addRunClasspathFiles(customLib)
- .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
- .assertSuccessWithOutput(
- StringUtils.lines(
- "Optional[value]",
- "OptionalDouble[1.0]",
- "OptionalInt[1]",
- "OptionalLong[1]",
- "Optional[value]",
- "value"));
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testRewrittenAPICallsR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Executor.class)
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
}
static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
index db5c661..5a03f73 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
@@ -7,12 +7,11 @@
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
-import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.TestDiagnosticMessages;
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
import java.time.Duration;
@@ -21,35 +20,94 @@
import java.time.MonthDay;
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.util.List;
+import org.junit.BeforeClass;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class AllTimeConversionTest extends DesugaredLibraryTestBase {
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "1970-01-02T00:00Z[GMT]",
+ "PT0.000012345S",
+ "GMT",
+ "--03-02",
+ "-1000000000-01-01T00:00:00.999999999Z",
+ "GMT",
+ "GMT");
+
+ private static Path CUSTOM_LIB;
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+ }
+
+ public AllTimeConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void compileCustomLib() throws Exception {
+ CUSTOM_LIB =
+ testForD8(getStaticTemp())
+ .addProgramClasses(CustomLibClass.class)
+ .setMinApi(MIN_SUPPORTED)
+ .compile()
+ .writeToZip();
+ }
+
@Test
- public void testRewrittenAPICalls() throws Exception {
- Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
- D8TestCompileResult compileResult =
- testForD8()
- .setMinApi(AndroidApiLevel.B)
- .addProgramClasses(Executor.class)
- .addLibraryClasses(CustomLibClass.class)
- .addOptionsModification(options -> options.testing.trackDesugaredAPIConversions = true)
- .enableCoreLibraryDesugaring(AndroidApiLevel.B)
- .compile();
- compileResult
- .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
- .addRunClasspathFiles(customLib)
- .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
- .assertSuccessWithOutput(
- StringUtils.lines(
- "1970-01-02T00:00Z[GMT]",
- "PT0.000012345S",
- "GMT",
- "--03-02",
- "-1000000000-01-01T00:00:00.999999999Z",
- "GMT",
- "GMT"));
- assertTrackedAPIS(compileResult.getDiagnosticMessages());
+ public void testRewrittenAPICallsD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .addOptionsModification(options -> options.testing.trackDesugaredAPIConversions = true)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .inspectDiagnosticMessages(this::assertTrackedAPIS)
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testRewrittenAPICallsR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Executor.class)
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .allowDiagnosticWarningMessages()
+ .addOptionsModification(options -> options.testing.trackDesugaredAPIConversions = true)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
}
private void assertTrackedAPIS(TestDiagnosticMessages diagnosticMessages) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
index f31ad69..4ac3ccc 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
@@ -4,33 +4,92 @@
package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
import java.time.MonthDay;
+import java.util.List;
+import org.junit.BeforeClass;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
// Longs and double take two stack indexes, this had to be dealt with in
// CfAPIConverter*WrapperCodeProvider (See stackIndex vs index), this class tests that the
// synthetic Cf code is correct.
+@RunWith(Parameterized.class)
public class BasicLongDoubleConversionTest extends DesugaredLibraryTestBase {
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
+ private static final String EXPECTED_RESULT = StringUtils.lines("--01-16");
+
+ private static Path CUSTOM_LIB;
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+ }
+
+ public BasicLongDoubleConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void compileCustomLib() throws Exception {
+ CUSTOM_LIB =
+ testForD8(getStaticTemp())
+ .addProgramClasses(CustomLibClass.class)
+ .setMinApi(MIN_SUPPORTED)
+ .compile()
+ .writeToZip();
+ }
+
@Test
- public void testRewrittenAPICalls() throws Exception {
- Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+ public void testRewrittenAPICallsD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
- .setMinApi(AndroidApiLevel.B)
+ .setMinApi(parameters.getApiLevel())
.addProgramClasses(Executor.class)
.addLibraryClasses(CustomLibClass.class)
- .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
- .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
- .addRunClasspathFiles(customLib)
- .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
- .assertSuccessWithOutput(StringUtils.lines("--01-16"));
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testRewrittenAPICallsR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class)
+ .addKeepMainRule(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
}
static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
index 1e66f26..bb728fb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
@@ -4,62 +4,85 @@
package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.L8Command;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.StringResource;
-import com.android.tools.r8.TestDiagnosticMessagesImpl;
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.nio.file.Path;
import java.time.ZoneId;
+import java.util.List;
import java.util.TimeZone;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class BasicTimeConversionTest extends DesugaredLibraryTestBase {
- @Test
- public void testTimeGeneratedDex() throws Exception {
- TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
- Path desugaredLib = temp.newFolder().toPath().resolve("conversion_dex.zip");
- L8Command.Builder l8Builder =
- L8Command.builder(diagnosticsHandler)
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
- .addDesugaredLibraryConfiguration(
- StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
- .setMinApiLevel(AndroidApiLevel.B.getLevel())
- .setOutput(desugaredLib, OutputMode.DexIndexed);
- ToolHelper.runL8(l8Builder.build(), x -> {});
- this.checkTimeConversionGeneratedDex(new CodeInspector(desugaredLib));
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final String GMT = StringUtils.lines("GMT");
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(AndroidApiLevel.O), BooleanUtils.values());
}
- private void checkTimeConversionGeneratedDex(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz("j$.time.TimeConversions");
- assertThat(clazz, isPresent());
- assertEquals(13, clazz.allMethods().size());
+ public BasicTimeConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
}
@Test
- public void testRewrittenAPICalls() throws Exception {
+ public void testRewrittenAPICallsD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
- .setMinApi(AndroidApiLevel.B)
+ .setMinApi(parameters.getApiLevel())
.addInnerClasses(BasicTimeConversionTest.class)
- .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
.inspect(this::checkAPIRewritten)
- .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
- .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class);
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(GMT);
+ if (shrinkDesugaredLibrary) {
+ checkKeepRules(keepRuleConsumer.get());
+ }
+ }
+
+ private void checkKeepRules(String keepRules) {
+ assertTrue(keepRules.contains("TimeConversion"));
+ }
+
+ @Test
+ public void testRewrittenAPICallsR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Executor.class)
+ .addInnerClasses(BasicTimeConversionTest.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(GMT);
+ if (shrinkDesugaredLibrary) {
+ checkKeepRules(keepRuleConsumer.get());
+ }
}
private void checkAPIRewritten(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
new file mode 100644
index 0000000..43d5b9a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ConversionsPresentTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public ConversionsPresentTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testConversionsDex() throws Exception {
+ TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+ Path desugaredLib = temp.newFolder().toPath().resolve("conversion_dex.zip");
+ L8Command.Builder l8Builder =
+ L8Command.builder(diagnosticsHandler)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+ .setMinApiLevel(parameters.getApiLevel().getLevel())
+ .setOutput(desugaredLib, OutputMode.DexIndexed);
+ ToolHelper.runL8(l8Builder.build(), x -> {});
+ this.checkConversionGeneratedDex(new CodeInspector(desugaredLib));
+ }
+
+ private void checkConversionGeneratedDex(CodeInspector inspector) {
+ List<FoundClassSubject> conversionsClasses =
+ inspector.allClasses().stream()
+ .filter(c -> c.getOriginalName().contains("Conversions"))
+ .collect(Collectors.toList());
+ if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+ assertEquals(5, conversionsClasses.size());
+ assertTrue(inspector.clazz("j$.util.OptionalConversions").isPresent());
+ assertTrue(inspector.clazz("j$.time.TimeConversions").isPresent());
+ assertTrue(inspector.clazz("j$.util.LongSummaryStatisticsConversions").isPresent());
+ assertTrue(inspector.clazz("j$.util.IntSummaryStatisticsConversions").isPresent());
+ assertTrue(inspector.clazz("j$.util.DoubleSummaryStatisticsConversions").isPresent());
+ } else if (parameters.getApiLevel().isLessThan(AndroidApiLevel.O)) {
+ assertEquals(1, conversionsClasses.size());
+ assertTrue(inspector.clazz("j$.time.TimeConversions").isPresent());
+ } else {
+ assertEquals(0, inspector.allClasses().size());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index dbdc654..a79ccf7 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.desugar.nestaccesscontrol;
import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -50,7 +51,9 @@
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR_11)
.addKeepRuleFiles(MAIN_KEEP)
.addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+ .allowDiagnosticWarningMessages()
.compile()
+ .assertAllWarningMessagesMatch(containsString("Missing class:"))
.inspect(this::assertNotEmpty)
.inspect(Java11R8CompilationTest::assertNoNests);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
index 48fe91b..75982e9 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
@@ -69,7 +69,11 @@
}
private TestCompileResult compileOnlyClassesMatching(
- Matcher<String> matcher, boolean d8, boolean ignoreMissingClasses) throws Exception {
+ Matcher<String> matcher,
+ boolean d8,
+ boolean allowDiagnosticWarningMessages,
+ boolean ignoreMissingClasses)
+ throws Exception {
List<Path> matchingClasses =
CLASS_NAMES.stream()
.filter(matcher::matches)
@@ -93,6 +97,7 @@
options.enableNestBasedAccessDesugaring = true;
options.ignoreMissingClasses = ignoreMissingClasses;
})
+ .allowDiagnosticWarningMessages(allowDiagnosticWarningMessages)
.compile();
}
}
@@ -101,7 +106,7 @@
try {
Matcher<String> innerClassMatcher =
containsString("BasicNestHostWithInnerClassMethods$BasicNestedClass");
- compileOnlyClassesMatching(innerClassMatcher, false, false);
+ compileOnlyClassesMatching(innerClassMatcher, false, false, false);
fail("Should have raised an exception for missing nest host");
} catch (Exception e) {
assertTrue(e.getCause().getCause().getMessage().contains("requires its nest host"));
@@ -111,7 +116,7 @@
private void testIncompleteNestError() {
try {
Matcher<String> innerClassMatcher = endsWith("BasicNestHostWithInnerClassMethods");
- compileOnlyClassesMatching(innerClassMatcher, false, false);
+ compileOnlyClassesMatching(innerClassMatcher, false, false, false);
fail("Should have raised an exception for incomplete nest");
} catch (Exception e) {
assertTrue(e.getCause().getCause().getMessage().contains("requires its nest mates"));
@@ -121,7 +126,7 @@
private void testMissingNestHostWarning(boolean d8, boolean desugarWarning) throws Exception {
Matcher<String> innerClassMatcher =
containsString("BasicNestHostWithInnerClassMethods$BasicNestedClass");
- TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, true);
+ TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true);
assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
if (desugarWarning) {
assertTrue(
@@ -137,7 +142,7 @@
private void testIncompleteNestWarning(boolean d8, boolean desugarWarning) throws Exception {
Matcher<String> innerClassMatcher = endsWith("BasicNestHostWithInnerClassMethods");
- TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, true);
+ TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true);
assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
if (desugarWarning) {
assertTrue(
@@ -145,9 +150,7 @@
.anyMatch(warn -> warn instanceof IncompleteNestNestDesugarDiagnosic));
} else if (!d8) {
// R8 should raise extra warning when cleaning the nest.
- assertTrue(
- compileResult.getDiagnosticMessages().getWarnings().stream()
- .anyMatch(warn -> warn.getDiagnosticMessage().contains("requires its nest mates")));
+ compileResult.assertWarningMessageThatMatches(containsString("requires its nest mates"));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingAnalysisTest.java
index 247e8a1..9c4872f 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingAnalysisTest.java
@@ -59,6 +59,7 @@
.enableInliningAnnotations()
.addKeepRules(KEEP_ENUM)
.addOptionsModification(this::enableEnumOptions)
+ .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::assertEnumsAsExpected);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java
index 762380a..e4f7bd1 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java
@@ -8,7 +8,6 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
@@ -46,16 +45,13 @@
@Test
public void testEnumUnboxingFailure() throws Exception {
- R8FullTestBuilder r8FullTestBuilder =
- testForR8(parameters.getBackend())
- .addInnerClasses(FailingMethodEnumUnboxingAnalysisTest.class);
- for (Class<?> failure : FAILURES) {
- r8FullTestBuilder.addKeepMainRule(failure);
- }
R8TestCompileResult compile =
- r8FullTestBuilder
+ testForR8(parameters.getBackend())
+ .addInnerClasses(FailingMethodEnumUnboxingAnalysisTest.class)
+ .addKeepMainRules(FAILURES)
.addKeepRules(KEEP_ENUM)
.addOptionsModification(this::enableEnumOptions)
+ .allowDiagnosticInfoMessages()
.enableInliningAnnotations()
.addOptionsModification(
// Disabled to avoid toString() being removed.
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java
index 2841689..8811ee5 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java
@@ -40,6 +40,7 @@
.addKeepMainRules(INPUTS)
.addKeepRules(KEEP_ENUM)
.addOptionsModification(this::enableEnumOptions)
+ .allowDiagnosticInfoMessages()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.noMinification()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingAnalysisTest.java
index 079c9f7..2570a12 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingAnalysisTest.java
@@ -37,6 +37,7 @@
.addKeepMainRule(classToTest)
.addKeepRules(KEEP_ENUM)
.addOptionsModification(this::enableEnumOptions)
+ .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
.inspectDiagnosticMessages(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingAnalysisTest.java
index 87cacd5..2abd358 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingAnalysisTest.java
@@ -39,6 +39,7 @@
.addKeepRules(KEEP_ENUM)
.enableInliningAnnotations()
.addOptionsModification(this::enableEnumOptions)
+ .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
.inspectDiagnosticMessages(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingAnalysisTest.java
index 699c72d..53cddc6 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingAnalysisTest.java
@@ -39,6 +39,7 @@
.addKeepRules(KEEP_ENUM)
.enableInliningAnnotations()
.addOptionsModification(this::enableEnumOptions)
+ .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
.inspectDiagnosticMessages(
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index 6bf5d8f..0788985 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.internal.proto;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -91,10 +93,15 @@
options.enableStringSwitchConversion = true;
})
.allowAccessModification()
+ .allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllInfoMessagesMatch(
+ containsString("Proguard configuration rule does not match anything"))
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.inspect(this::inspect);
for (String main : mains) {
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index 558664f..1473e03 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -5,6 +5,9 @@
package com.android.tools.r8.internal.proto;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -88,10 +91,15 @@
options.enableStringSwitchConversion = true;
})
.allowAccessModification(allowAccessModification)
+ .allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
.minification(enableMinification)
.setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllInfoMessagesMatch(
+ containsString("Proguard configuration rule does not match anything"))
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.inspect(
outputInspector -> {
verifyMapAndRequiredFieldsAreKept(inputInspector, outputInspector);
@@ -356,10 +364,17 @@
options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
})
.allowAccessModification(allowAccessModification)
+ .allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
.minification(enableMinification)
.setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllInfoMessagesMatch(
+ containsString("Proguard configuration rule does not match anything"))
+ .assertAllWarningMessagesMatch(
+ anyOf(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
+ containsString("required for default or static interface methods desugaring")))
.inspect(
inspector ->
assertRewrittenProtoSchemasMatch(new CodeInspector(PROGRAM_FILES), inspector));
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
index faa91e5..7a261ff 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
@@ -5,6 +5,9 @@
package com.android.tools.r8.internal.proto;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -64,10 +67,14 @@
options.enableStringSwitchConversion = true;
})
.allowAccessModification(allowAccessModification)
+ .allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
.minification(enableMinification)
.setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllInfoMessagesMatch(
+ containsString("Proguard configuration rule does not match anything"))
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.inspect(
outputInspector -> {
verifyUnusedFieldsAreRemoved(inputInspector, outputInspector);
@@ -113,10 +120,17 @@
options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
})
.allowAccessModification(allowAccessModification)
+ .allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
.minification(enableMinification)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllInfoMessagesMatch(
+ containsString("Proguard configuration rule does not match anything"))
+ .assertAllWarningMessagesMatch(
+ anyOf(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
+ containsString("required for default or static interface methods desugaring")))
.inspect(
inspector ->
assertRewrittenProtoSchemasMatch(new CodeInspector(PROGRAM_FILES), inspector));
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 02395ef..979de40 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
@@ -81,14 +82,16 @@
public void testOptimizationInfo() throws Exception {
AppView<AppInfoWithSubtyping> appView = buildApp();
OptimizationFeedbackMock feedback = new OptimizationFeedbackMock();
- FieldBitAccessAnalysis fieldBitAccessAnalysis = new FieldBitAccessAnalysis(appView);
+ FieldBitAccessAnalysis fieldBitAccessAnalysis = new FieldBitAccessAnalysis();
+ FieldAccessAnalysis fieldAccessAnalysis =
+ new FieldAccessAnalysis(appView, null, fieldBitAccessAnalysis);
DexProgramClass clazz = appView.appInfo().classes().iterator().next();
assertEquals(TestClass.class.getTypeName(), clazz.type.toSourceString());
for (DexEncodedMethod method : clazz.methods()) {
IRCode code = method.buildIR(appView, Origin.unknown());
- fieldBitAccessAnalysis.recordFieldAccesses(code, feedback);
+ fieldAccessAnalysis.recordFieldAccesses(code, feedback, new MethodProcessorMock());
}
int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
@@ -205,6 +208,19 @@
}
}
+ static class MethodProcessorMock implements MethodProcessor {
+
+ @Override
+ public Phase getPhase() {
+ return Phase.PRIMARY;
+ }
+
+ @Override
+ public boolean isProcessedConcurrently(DexEncodedMethod method) {
+ return false;
+ }
+ }
+
static class OptimizationFeedbackMock extends OptimizationFeedbackIgnore {
Reference2IntMap<DexEncodedField> bitsReadPerField = new Reference2IntOpenHashMap<>();
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
index 901a577..41ce8a8 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
@@ -5,19 +5,20 @@
package com.android.tools.r8.ir.analysis.type;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.NeverMerge;
-import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.base.Throwables;
-import java.nio.file.Path;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,7 +36,8 @@
@Parameters(name = "{1}, allow type errors: {0}")
public static List<Object[]> data() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
public MissingClassesJoinTest(boolean allowTypeErrors, TestParameters parameters) {
@@ -45,57 +47,63 @@
@Test
public void test() throws Exception {
- Path classpathFile =
- testForD8()
- .addProgramClasses(ASub2.class)
- .setMinApi(parameters.getRuntime())
- .compile()
- .writeToZip();
-
if (parameters.isDexRuntime() && !allowTypeErrors) {
- testForD8()
- // Intentionally not adding ASub2 as a program class.
- .addProgramClasses(A.class, ASub1.class, Box.class, TestClass.class)
- .setMinApi(parameters.getRuntime())
- .compile()
- .addRunClasspathFiles(classpathFile)
+ D8TestCompileResult compileResult =
+ testForD8()
+ // Intentionally not adding ASub2 as a program class.
+ .addProgramClasses(A.class, ASub1.class, Box.class, TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ testForRuntime(parameters)
+ .addProgramFiles(compileResult.writeToZip())
+ .addProgramClasses(ASub2.class)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
}
try {
- R8TestRunResult result =
+ R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
// Intentionally not adding ASub2 as a program class.
.addProgramClasses(A.class, ASub1.class, Box.class, TestClass.class)
.addKeepAllClassesRule()
.addOptionsModification(options -> options.testing.allowTypeErrors = allowTypeErrors)
+ .allowDiagnosticWarningMessages()
.enableMergeAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
- .addRunClasspathFiles(classpathFile)
- .run(parameters.getRuntime(), TestClass.class);
+ .assertAllWarningMessagesMatch(
+ equalTo(
+ "The method `void "
+ + TestClass.class.getTypeName()
+ + ".main(java.lang.String[])` does not type check and will be assumed to"
+ + " be unreachable."));
// Compilation fails unless type errors are allowed.
assertTrue(allowTypeErrors);
- // TestClass.main() does not type check, so it should have been replaced by `throw null`.
- // Note that, even if we do not replace the body of main() with `throw null`, the code would
- // still not work for the CF backend:
- //
- // java.lang.VerifyError: Bad type on operand stack
- // Exception Details:
- // Location:
- // MissingClassesJoinTest$TestClass.main([Ljava/lang/String;)V @28: putstatic
- // Reason:
- // Type 'java/lang/Object' (current frame, stack[0]) is not assignable to
- // 'com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest$A'
- // Current Frame:
- // bci: @28
- // flags: { }
- // locals: { 'java/lang/Object' }
- // stack: { 'java/lang/Object' }
- result.assertFailureWithErrorThatMatches(containsString("NullPointerException"));
+ testForRuntime(parameters)
+ .addProgramFiles(compileResult.writeToZip())
+ .addProgramClasses(ASub2.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ // TestClass.main() does not type check, so it should have been replaced by `throw null`.
+ // Note that, even if we do not replace the body of main() with `throw null`, the code
+ // would still not work for the CF backend:
+ //
+ // java.lang.VerifyError: Bad type on operand stack
+ // Exception Details:
+ // Location:
+ // MissingClassesJoinTest$TestClass.main([Ljava/lang/String;)V @28: putstatic
+ // Reason:
+ // Type 'java/lang/Object' (current frame, stack[0]) is not assignable to
+ // 'com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest$A'
+ // Current Frame:
+ // bci: @28
+ // flags: { }
+ // locals: { 'java/lang/Object' }
+ // stack: { 'java/lang/Object' }
+ .assertFailureWithErrorThatMatches(containsString("NullPointerException"));
} catch (CompilationFailedException e) {
// Compilation should only fail when type errors are not allowed.
assertFalse(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithInheritanceTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithInheritanceTest.java
index a1042ce..73d694e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithInheritanceTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithInheritanceTest.java
@@ -40,10 +40,9 @@
testForR8(parameters.getBackend())
.addInnerClasses(BuilderWithInheritanceTest.class)
.addKeepMainRule(TestClass.class)
- // TODO(b/145090972): Should never need to exit gracefully during testing.
- .allowClassInlinerGracefulExit()
.enableInliningAnnotations()
.enableMergeAnnotations()
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput("42")
.inspector();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.java
index 6d70ba5..34e9d5d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.java
@@ -40,7 +40,6 @@
ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.class,
ClassInlineInstanceInitializerWithInaccessibleStaticGetTestClasses.class)
.addKeepMainRule(TestClass.class)
- .allowClassInlinerGracefulExit()
.enableInliningAnnotations()
.enableMergeAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
index 08d4f85..98a6e9e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
@@ -4,25 +4,21 @@
package com.android.tools.r8.ir.optimize.inliner;
-import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.Streams;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-
@RunWith(Parameterized.class)
public class Regress131349148 extends TestBase {
@@ -60,12 +56,18 @@
public void testNoInlineNonExistingCatchPreL() throws Exception {
R8TestRunResult result =
testForR8(parameters.getBackend())
- .addProgramClasses(TestClassCallingMethodWithNonExisting.class,
- ClassWithCatchNonExisting.class, ExistingException.class)
+ .addProgramClasses(
+ TestClassCallingMethodWithNonExisting.class,
+ ClassWithCatchNonExisting.class,
+ ExistingException.class)
.addKeepMainRule(TestClassCallingMethodWithNonExisting.class)
.addKeepRules("-dontwarn " + NonExistingException.class.getTypeName())
+ .allowDiagnosticWarningMessages(
+ parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
.setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllWarningMessagesMatch(
+ containsString("required for default or static interface methods desugaring"))
.run(parameters.getRuntime(), TestClassCallingMethodWithNonExisting.class)
.assertSuccess();
ClassSubject classSubject =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/PrunedClassNameComparisonTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/PrunedClassNameComparisonTest.java
new file mode 100644
index 0000000..5dc83d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/PrunedClassNameComparisonTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class PrunedClassNameComparisonTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public PrunedClassNameComparisonTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(PrunedClassNameComparisonTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Live!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+ assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent());
+ assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent()));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ if (get()
+ .getClass()
+ .getName()
+ .equals("com.android.tools.r8.ir.optimize.library.PrunedClassNameComparisonTest$C")) {
+ dead();
+ } else {
+ live();
+ }
+ }
+
+ static I get() {
+ return System.currentTimeMillis() >= 0 ? new A() : new B();
+ }
+
+ @NeverInline
+ static void live() {
+ System.out.println("Live!");
+ }
+
+ @NeverInline
+ static void dead() {
+ System.out.println("Dead!");
+ }
+ }
+
+ interface I {}
+
+ static class A implements I {}
+
+ static class B implements I {}
+
+ static class C implements I {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentTest.java
new file mode 100644
index 0000000..3eb649a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentTest.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class FieldWithDefaultValueAssignmentTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public FieldWithDefaultValueAssignmentTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(FieldWithDefaultValueAssignmentTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Live!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+ assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent());
+ assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent()));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Config config = Config.create();
+ if (config.alwaysFalse) {
+ dead();
+ } else {
+ live();
+ }
+ }
+
+ @NeverInline
+ static void live() {
+ System.out.println("Live!");
+ }
+
+ @NeverInline
+ static void dead() {
+ System.out.println("Dead!");
+ }
+ }
+
+ static class Config {
+
+ boolean alwaysFalse;
+
+ @NeverInline
+ static Config create() {
+ Config config = new Config();
+ config.alwaysFalse = false;
+ return config;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java
index 4e4624a..86745e5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java
@@ -4,10 +4,13 @@
package com.android.tools.r8.ir.optimize.redundantfieldloadelimination;
+import static org.hamcrest.CoreMatchers.containsString;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -28,6 +31,7 @@
return getTestParameters()
.withCfRuntimes()
.withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.O)
.build();
}
@@ -40,8 +44,10 @@
testForR8(parameters.getBackend())
.addProgramClassFileData(InstanceFieldLoadsSeparatedByInvokeCustomTestClassGenerator.dump())
.addKeepAllClassesRule()
- .setMinApi(parameters.getRuntime())
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllWarningMessagesMatch(containsString("Unknown bootstrap method"))
.run(parameters.getRuntime(), "InstanceFieldLoadsSeparatedByInvokeCustomTestClass")
.assertSuccess();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
index 8896197..8b33acb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
@@ -143,7 +143,7 @@
@Parameters(name = "{0} minify:{1} {2}")
public static Collection<Object[]> parameters() {
return buildParameters(
- getTestParameters().withAllRuntimes().build(),
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
BooleanUtils.values(),
TestNamingConfig.values());
}
@@ -159,6 +159,23 @@
this.config = config;
}
+ public boolean hasMalformedInnerClassAttribute() {
+ switch (config) {
+ case DEFAULT:
+ case OUTER_ENDS_WITH_DOLLAR:
+ case $_$_$:
+ case DOLLAR2_SEPARATOR:
+ return false;
+ case EMTPY_SEPARATOR:
+ case UNDERBAR_SEPARATOR:
+ case NON_NESTED_INNER:
+ case WRONG_REPACKAGE:
+ return true;
+ default:
+ throw new Unreachable("Unexpected test configuration: " + config);
+ }
+ }
+
private void checkWarningsAboutMalformedAttribute(TestCompileResult<?, ?> result) {
switch (config) {
case DEFAULT:
@@ -188,7 +205,7 @@
testForD8()
.addProgramClassFileData(InnerClassNameTestDump.dump(config, parameters))
.addOptionsModification(InternalOptions::disableNameReflectionOptimization)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile();
checkWarningsAboutMalformedAttribute(d8CompileResult);
D8TestRunResult d8RunResult = d8CompileResult.run(parameters.getRuntime(), MAIN_CLASS);
@@ -211,11 +228,13 @@
.addKeepRules("-keep,allowobfuscation class * { *; }")
.addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
.addProgramClassFileData(InnerClassNameTestDump.dump(config, parameters))
+ .allowDiagnosticInfoMessages(hasMalformedInnerClassAttribute())
.minification(minify)
.addOptionsModification(InternalOptions::disableNameReflectionOptimization)
- .setMinApi(parameters.getRuntime())
- .compile();
- checkWarningsAboutMalformedAttribute(r8CompileResult);
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .apply(this::checkWarningsAboutMalformedAttribute);
+
CodeInspector inspector = r8CompileResult.inspector();
R8TestRunResult r8RunResult = r8CompileResult.run(parameters.getRuntime(), MAIN_CLASS);
switch (config) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
index c13b942..c8d3f1c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.JvmTestRunResult;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
@@ -22,28 +23,21 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class StringLengthTest extends TestBase {
- private static final String JAVA_OUTPUT = StringUtils.lines(
- "4",
- "6",
- "Shared",
- "14",
- "Another_shared",
- "2",
- "1",
- "𐀀", // Different output in Windows.
- "3"
- );
+ private static final String JAVA_OUTPUT =
+ StringUtils.lines("4", "6", "Shared", "14", "Another_shared", "2", "1", "🂡", "3");
private static final Class<?> MAIN = TestClass.class;
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -55,12 +49,10 @@
@Test
public void testJVMOutput() throws Exception {
assumeTrue("Only run JVM reference on CF runtimes", parameters.isCfRuntime());
- // TODO(b/119097175)
+ JvmTestRunResult run = testForJvm().addTestClasspath().run(parameters.getRuntime(), MAIN);
+ // TODO(b/119097175): Fix test
if (!ToolHelper.isWindows()) {
- testForJvm()
- .addTestClasspath()
- .run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(JAVA_OUTPUT);
+ run.assertSuccessWithOutput(JAVA_OUTPUT);
}
}
@@ -83,30 +75,23 @@
@Test
public void testD8() throws Exception {
assumeTrue("Only run D8 for Dex backend", parameters.isDexRuntime());
-
D8TestRunResult result =
testForD8()
.release()
.addProgramClasses(MAIN)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN);
- // TODO(b/119097175)
- if (!ToolHelper.isWindows()) {
- result.assertSuccessWithOutput(JAVA_OUTPUT);
- }
- test(result, 1, 4);
+ result.assertSuccessWithOutput(JAVA_OUTPUT);
+ test(result, 1, 5);
result =
testForD8()
.debug()
.addProgramClasses(MAIN)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN);
- // TODO(b/119097175)
- if (!ToolHelper.isWindows()) {
- result.assertSuccessWithOutput(JAVA_OUTPUT);
- }
- test(result, 6, 0);
+ result.assertSuccessWithOutput(JAVA_OUTPUT);
+ test(result, 6, 1);
}
@Test
@@ -116,16 +101,16 @@
.addProgramClasses(MAIN)
.enableInliningAnnotations()
.addKeepMainRule(MAIN)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN);
- // TODO(b/119097175)
+ // TODO(b/119097175): Fix test
if (!ToolHelper.isWindows()) {
result.assertSuccessWithOutput(JAVA_OUTPUT);
+ test(result, 0, parameters.isDexRuntime() ? 6 : 7);
}
- test(result, 0, parameters.isDexRuntime() ? 5 : 6);
}
- static class TestClass {
+ public static class TestClass {
@ForceInline
static String simpleInlineable() {
@@ -139,7 +124,7 @@
return n.length();
}
- public static void main(String[] args) {
+ public static void main(String[] args) throws UnsupportedEncodingException {
String s1 = "GONE";
// Can be computed at compile time: constCount++
System.out.println(s1.length());
@@ -155,12 +140,14 @@
System.out.println(s4.length());
System.out.println(s4);
- String s5 = "\uD800\uDC00"; // U+10000
+ String s5 = "\uD83C\uDCA1"; // U+1F0A1
// Can be computed at compile time: constCount++
System.out.println(s5.length());
// Even reusable: should not increase any counts.
System.out.println(s5.codePointCount(0, s5.length()));
- System.out.println(s5);
+ // The true below will add to the constant count.
+ PrintStream ps = new PrintStream(System.out, true, "UTF-8");
+ ps.println(s5);
// Make sure this is not optimized in DEBUG mode.
int l = "ABC".length();
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
index 1c46c19..4062cb0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.kotlin.lambda;
import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestParameters;
@@ -54,11 +56,14 @@
.addLibraryFiles(ToolHelper.getKotlinStdlibJar())
.addProgramFiles(ktClasses)
.addKeepMainRule("**.B143165163Kt")
+ .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
// TODO(b/143165163): better not output info like this.
- .assertInfoMessageThatMatches(containsString("Unrecognized Kotlin lambda"))
- .assertInfoMessageThatMatches(containsString("unexpected static method"))
+ .assertAllInfoMessagesMatch(
+ allOf(
+ containsString("Unrecognized Kotlin lambda"),
+ containsString("unexpected static method")))
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar())
.run(parameters.getRuntime(), pkg + ".B143165163Kt")
.assertSuccessWithOutputLines("outer foo bar", "outer foo default");
@@ -80,11 +85,15 @@
.addProgramFiles(ToolHelper.getKotlinStdlibJar())
.addProgramFiles(ktClasses)
.addKeepMainRule("**.B143165163Kt")
+ .allowDiagnosticMessages()
.setMinApi(parameters.getApiLevel())
.compile()
// TODO(b/143165163): better not output info like this.
- .assertInfoMessageThatMatches(containsString("Unrecognized Kotlin lambda"))
- .assertInfoMessageThatMatches(containsString("does not implement any interfaces"))
+ .assertAllInfoMessagesMatch(
+ allOf(
+ containsString("Unrecognized Kotlin lambda"),
+ containsString("does not implement any interfaces")))
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), pkg + ".B143165163Kt")
.assertSuccessWithOutputLines("outer foo bar", "outer foo default");
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index d866cca..562fdbb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -13,7 +13,6 @@
super(targetVersion);
}
- static final String PKG_PREFIX =
- DescriptorUtils.getBinaryNameFromJavaType(
- KotlinMetadataTestBase.class.getPackage().getName());
+ static final String PKG = KotlinMetadataTestBase.class.getPackage().getName();
+ static final String PKG_PREFIX = DescriptorUtils.getBinaryNameFromJavaType(PKG);
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
deleted file mode 100644
index e785965..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright (c) 2019, 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInClasspathTypeTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInClasspathTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path baseLibJar;
- private static Path extLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String baseLibFolder = PKG_PREFIX + "/classpath_lib_base";
- baseLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(baseLibFolder, "itf"))
- .compile();
- String extLibFolder = PKG_PREFIX + "/classpath_lib_ext";
- extLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(baseLibJar)
- .addSourceFiles(getKotlinFileInTest(extLibFolder, "impl"))
- .compile();
- }
-
- @Test
- public void testMetadataInClasspathType_merged() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addClasspathFiles(baseLibJar)
- .addProgramFiles(extLibJar)
- // Keep the Extra class and its interface (which has the method).
- .addKeepRules("-keep class **.Extra")
- // Keep the ImplKt extension method which requires metadata
- // to be called with Kotlin syntax from other kotlin code.
- .addKeepRules("-keep class **.ImplKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String implClassName = pkg + ".classpath_lib_ext.Impl";
- final String implKtClassName = pkg + ".classpath_lib_ext.ImplKt";
- final String extraClassName = pkg + ".classpath_lib_ext.Extra";
- compileResult.inspect(inspector -> {
- assertThat(inspector.clazz(implClassName), not(isPresent()));
-
- ClassSubject implKt = inspector.clazz(implKtClassName);
- assertThat(implKt, isPresent());
- assertThat(implKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = implKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
- assertThat(kmFunction, isPresent());
-
- ClassSubject extra = inspector.clazz(extraClassName);
- assertThat(extra, isPresent());
- assertThat(extra, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = extra.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Impl")));
- // Can't build ClassSubject with Itf in classpath. Instead, check if the reference to Itf is
- // not altered via descriptors.
- List<String> superTypeDescriptors = kmClass.getSuperTypeDescriptors();
- assertTrue(superTypeDescriptors.stream().noneMatch(supertype -> supertype.contains("Impl")));
- assertTrue(superTypeDescriptors.stream().anyMatch(supertype -> supertype.contains("Itf")));
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/classpath_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(baseLibJar, libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".classpath_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo");
- }
-
- @Test
- public void testMetadataInClasspathType_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addClasspathFiles(baseLibJar)
- .addProgramFiles(extLibJar)
- // Keep the Extra class and its interface (which has the method).
- .addKeepRules("-keep class **.Extra")
- // Keep Super, but allow minification.
- .addKeepRules("-keep,allowobfuscation class **.Impl")
- // Keep the ImplKt extension method which requires metadata
- // to be called with Kotlin syntax from other kotlin code.
- .addKeepRules("-keep class **.ImplKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String implClassName = pkg + ".classpath_lib_ext.Impl";
- final String implKtClassName = pkg + ".classpath_lib_ext.ImplKt";
- final String extraClassName = pkg + ".classpath_lib_ext.Extra";
- compileResult.inspect(inspector -> {
- ClassSubject impl = inspector.clazz(implClassName);
- assertThat(impl, isRenamed());
-
- ClassSubject implKt = inspector.clazz(implKtClassName);
- assertThat(implKt, isPresent());
- assertThat(implKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = implKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
- assertThat(kmFunction, isExtensionFunction());
-
- ClassSubject extra = inspector.clazz(extraClassName);
- assertThat(extra, isPresent());
- assertThat(extra, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = extra.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Impl")));
- assertTrue(superTypes.stream().anyMatch(
- supertype -> supertype.getFinalDescriptor().equals(impl.getFinalDescriptor())));
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/classpath_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(baseLibJar, libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".classpath_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo");
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInCompanionTest.java
deleted file mode 100644
index a5c01a5..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInCompanionTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInCompanionTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInCompanionTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path companionLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String companionLibFolder = PKG_PREFIX + "/companion_lib";
- companionLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(companionLibFolder, "lib"))
- .compile();
- }
-
- @Test
- public void testMetadataInCompanion_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(companionLibJar)
- // Keep the B class and its interface (which has the doStuff method).
- .addKeepRules("-keep class **.B")
- .addKeepRules("-keep class **.I { <methods>; }")
- // Keep getters for B$Companion.(singleton|foo) which will be referenced at the app.
- .addKeepRules("-keepclassmembers class **.B$* { *** get*(...); }")
- // No rule for Super, but will be kept and renamed.
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String superClassName = pkg + ".companion_lib.Super";
- final String bClassName = pkg + ".companion_lib.B";
- final String companionClassName = pkg + ".companion_lib.B$Companion";
- compileResult.inspect(inspector -> {
- ClassSubject sup = inspector.clazz(superClassName);
- assertThat(sup, isRenamed());
-
- ClassSubject impl = inspector.clazz(bClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Super")));
- assertTrue(superTypes.stream().anyMatch(
- supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
-
- // Bridge for the property in the companion that needs a backing field.
- MethodSubject singletonBridge = impl.uniqueMethodWithName("access$getSingleton$cp");
- assertThat(singletonBridge, isRenamed());
-
- // For B$Companion.foo, no backing field needed, hence no bridge.
- MethodSubject fooBridge = impl.uniqueMethodWithName("access$getFoo$cp");
- assertThat(fooBridge, not(isPresent()));
-
- ClassSubject companion = inspector.clazz(companionClassName);
- assertThat(companion, isRenamed());
-
- MethodSubject singletonGetter = companion.uniqueMethodWithName("getSingleton");
- assertThat(singletonGetter, isPresent());
-
- MethodSubject fooGetter = companion.uniqueMethodWithName("getFoo");
- assertThat(fooGetter, isPresent());
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/companion_app";
- ProcessResult kotlinTestCompileResult =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- // TODO(b/143687784): update to just .compile() once fixed.
- .compileRaw();
-
- // TODO(b/70169921): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: singleton"));
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: foo"));
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java
deleted file mode 100644
index 0f124e4..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (c) 2019, 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInExtensionFunctionTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInExtensionFunctionTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path extLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String extLibFolder = PKG_PREFIX + "/extension_function_lib";
- extLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(extLibFolder, "B"))
- .compile();
- }
-
- @Test
- public void testMetadataInExtensionFunction_merged() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(extLibJar)
- // Keep the B class and its interface (which has the doStuff method).
- .addKeepRules("-keep class **.B")
- .addKeepRules("-keep class **.I { <methods>; }")
- // Keep the BKt extension function which requires metadata
- // to be called with Kotlin syntax from other kotlin code.
- .addKeepRules("-keep class **.BKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String superClassName = pkg + ".extension_function_lib.Super";
- final String bClassName = pkg + ".extension_function_lib.B";
- final String bKtClassName = pkg + ".extension_function_lib.BKt";
- compileResult.inspect(inspector -> {
- assertThat(inspector.clazz(superClassName), not(isPresent()));
-
- ClassSubject impl = inspector.clazz(bClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Super")));
-
- ClassSubject bKt = inspector.clazz(bKtClassName);
- assertThat(bKt, isPresent());
- assertThat(bKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = bKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
- assertThat(kmFunction, isExtensionFunction());
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/extension_function_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".extension_function_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
- }
-
- @Test
- public void testMetadataInExtensionFunction_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(extLibJar)
- // Keep the B class and its interface (which has the doStuff method).
- .addKeepRules("-keep class **.B")
- .addKeepRules("-keep class **.I { <methods>; }")
- // Keep Super, but allow minification.
- .addKeepRules("-keep,allowobfuscation class **.Super")
- // Keep the BKt extension function which requires metadata
- // to be called with Kotlin syntax from other kotlin code.
- .addKeepRules("-keep class **.BKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String superClassName = pkg + ".extension_function_lib.Super";
- final String bClassName = pkg + ".extension_function_lib.B";
- final String bKtClassName = pkg + ".extension_function_lib.BKt";
- compileResult.inspect(inspector -> {
- ClassSubject sup = inspector.clazz(superClassName);
- assertThat(sup, isPresent());
- assertThat(sup, isRenamed());
-
- ClassSubject impl = inspector.clazz(bClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Super")));
- assertTrue(superTypes.stream().anyMatch(
- supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
-
- ClassSubject bKt = inspector.clazz(bKtClassName);
- assertThat(bKt, isPresent());
- assertThat(bKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = bKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
- assertThat(kmFunction, isExtensionFunction());
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/extension_function_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".extension_function_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java
deleted file mode 100644
index 84d9dd2..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInExtensionPropertyTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInExtensionPropertyTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path extLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String extLibFolder = PKG_PREFIX + "/extension_property_lib";
- extLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(extLibFolder, "B"))
- .compile();
- }
-
- @Test
- public void testMetadataInExtensionProperty_merged() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(extLibJar)
- // Keep the B class and its interface (which has the doStuff method).
- .addKeepRules("-keep class **.B")
- .addKeepRules("-keep class **.I { <methods>; }")
- // Keep the BKt extension property which requires metadata
- // to be called with Kotlin syntax from other kotlin code.
- .addKeepRules("-keep class **.BKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String superClassName = pkg + ".extension_property_lib.Super";
- final String bClassName = pkg + ".extension_property_lib.B";
- final String bKtClassName = pkg + ".extension_property_lib.BKt";
- compileResult.inspect(inspector -> {
- assertThat(inspector.clazz(superClassName), not(isPresent()));
-
- ClassSubject impl = inspector.clazz(bClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Super")));
- KmFunctionSubject kmFunction = kmClass.kmFunctionWithUniqueName("doStuff");
- assertThat(kmFunction, isPresent());
- assertThat(kmFunction, not(isExtensionFunction()));
-
- ClassSubject bKt = inspector.clazz(bKtClassName);
- assertThat(bKt, isPresent());
- assertThat(bKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = bKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI");
- assertThat(kmProperty, isExtensionProperty());
- assertNotNull(kmProperty.getterSignature());
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/extension_property_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".extension_property_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
- }
-
- @Test
- public void testMetadataInExtensionProperty_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(extLibJar)
- // Keep the B class and its interface (which has the doStuff method).
- .addKeepRules("-keep class **.B")
- .addKeepRules("-keep class **.I { <methods>; }")
- // Keep Super, but allow minification.
- .addKeepRules("-keep,allowobfuscation class **.Super")
- // Keep the BKt extension property which requires metadata
- // to be called with Kotlin syntax from other kotlin code.
- .addKeepRules("-keep class **.BKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String superClassName = pkg + ".extension_property_lib.Super";
- final String bClassName = pkg + ".extension_property_lib.B";
- final String bKtClassName = pkg + ".extension_property_lib.BKt";
- compileResult.inspect(inspector -> {
- ClassSubject sup = inspector.clazz(superClassName);
- assertThat(sup, isRenamed());
-
- ClassSubject impl = inspector.clazz(bClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Super")));
- assertTrue(superTypes.stream().anyMatch(
- supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
-
- ClassSubject bKt = inspector.clazz(bKtClassName);
- assertThat(bKt, isPresent());
- assertThat(bKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = bKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI");
- assertThat(kmProperty, isExtensionProperty());
- assertNotNull(kmProperty.getterSignature());
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/extension_property_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".extension_property_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
- }
-}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java
deleted file mode 100644
index c0a1092..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInFunctionTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInFunctionTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path funLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String funLibFolder = PKG_PREFIX + "/function_lib";
- funLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(funLibFolder, "B"))
- .compile();
- }
-
- @Test
- public void testMetadataInFunction_merged() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(funLibJar)
- // Keep the B class and its interface (which has the doStuff method).
- .addKeepRules("-keep class **.B")
- .addKeepRules("-keep class **.I { <methods>; }")
- // Keep the BKt method, which will be called from other kotlin code.
- .addKeepRules("-keep class **.BKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String superClassName = pkg + ".function_lib.Super";
- final String bClassName = pkg + ".function_lib.B";
- final String bKtClassName = pkg + ".function_lib.BKt";
- compileResult.inspect(inspector -> {
- assertThat(inspector.clazz(superClassName), not(isPresent()));
-
- ClassSubject impl = inspector.clazz(bClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Super")));
-
- ClassSubject bKt = inspector.clazz(bKtClassName);
- assertThat(bKt, isPresent());
- assertThat(bKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = bKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun");
- assertThat(kmFunction, isPresent());
- assertThat(kmFunction, not(isExtensionFunction()));
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/function_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".function_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
- }
-
- @Test
- public void testMetadataInFunction_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(funLibJar)
- // Keep the B class and its interface (which has the doStuff method).
- .addKeepRules("-keep class **.B")
- .addKeepRules("-keep class **.I { <methods>; }")
- // Keep Super, but allow minification.
- .addKeepRules("-keep,allowobfuscation class **.Super")
- // Keep the BKt method, which will be called from other kotlin code.
- .addKeepRules("-keep class **.BKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String superClassName = pkg + ".function_lib.Super";
- final String bClassName = pkg + ".function_lib.B";
- final String bKtClassName = pkg + ".function_lib.BKt";
- compileResult.inspect(inspector -> {
- ClassSubject sup = inspector.clazz(superClassName);
- assertThat(sup, isRenamed());
-
- ClassSubject impl = inspector.clazz(bClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Super")));
- assertTrue(superTypes.stream().anyMatch(
- supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
-
- ClassSubject bKt = inspector.clazz(bKtClassName);
- assertThat(bKt, isPresent());
- assertThat(bKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = bKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun");
- assertThat(kmFunction, isPresent());
- assertThat(kmFunction, not(isExtensionFunction()));
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/function_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".function_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java
deleted file mode 100644
index 8917ebe..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInLibraryTypeTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInLibraryTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path baseLibJar;
- private static Path extLibJar;
- private static Path appJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String baseLibFolder = PKG_PREFIX + "/libtype_lib_base";
- baseLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(baseLibFolder, "base"))
- .compile();
- String extLibFolder = PKG_PREFIX + "/libtype_lib_ext";
- extLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(baseLibJar)
- .addSourceFiles(getKotlinFileInTest(extLibFolder, "ext"))
- .compile();
- String appFolder = PKG_PREFIX + "/libtype_app";
- appJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(baseLibJar)
- .addClasspathFiles(extLibJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .compile();
- }
-
- @Test
- public void testR8() throws Exception {
- String pkg = getClass().getPackage().getName();
- String main = pkg + ".libtype_app.MainKt";
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- // Intentionally not providing basLibJar as lib file nor classpath file.
- .addProgramFiles(extLibJar, appJar)
- // Keep Ext extension method which requires metadata to be called with Kotlin syntax
- // from other kotlin code.
- .addKeepRules("-keep class **.ExtKt { <methods>; }")
- // Keep the main entry.
- .addKeepMainRule(main)
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- // -dontoptimize so that basic code structure is kept.
- .noOptimization()
- .compile();
- final String extClassName = pkg + ".libtype_lib_ext.ExtKt";
- compileResult.inspect(inspector -> {
- ClassSubject ext = inspector.clazz(extClassName);
- assertThat(ext, isPresent());
- assertThat(ext, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = ext.getKmPackage();
- assertThat(kmPackage, isPresent());
- // Type appearance of library type, Base, should be kept, even if it's not provided.
- // Note that the resulting ClassSubject for Base is an absent one as we don't provide it, and
- // thus we can't use `getReturnTypesInFunctions`, which filters out absent class subject.
- assertTrue(kmPackage.getReturnTypeDescriptorsInFunctions().stream().anyMatch(
- returnType -> returnType.contains("Base")));
- });
-
- Path out = compileResult.writeToZip();
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar)
- .addClasspath(out)
- .run(parameters.getRuntime(), main)
- .assertSuccessWithOutputLines("Sub::foo", "Sub::boo", "true");
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
deleted file mode 100644
index 327878a..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (c) 2019, 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInMultifileClassTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInMultifileClassTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path multifileLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String multifileLibFolder = PKG_PREFIX + "/multifileclass_lib";
- multifileLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(
- getKotlinFileInTest(multifileLibFolder, "signed"),
- getKotlinFileInTest(multifileLibFolder, "unsigned"))
- .compile();
- }
-
- @Test
- public void testMetadataInMultifileClass_merged() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(multifileLibJar)
- // Keep UtilKt#comma*Join*(). Let R8 optimize (inline) others, such as joinOf*(String).
- .addKeepRules("-keep class **.UtilKt")
- .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String utilClassName = pkg + ".multifileclass_lib.UtilKt";
- final String signedClassName = pkg + ".multifileclass_lib.UtilKt__SignedKt";
- compileResult.inspect(inspector -> {
- ClassSubject util = inspector.clazz(utilClassName);
- assertThat(util, isPresent());
- assertThat(util, not(isRenamed()));
- MethodSubject commaJoinOfInt = util.uniqueMethodWithName("commaSeparatedJoinOfInt");
- assertThat(commaJoinOfInt, isPresent());
- assertThat(commaJoinOfInt, not(isRenamed()));
- MethodSubject joinOfInt = util.uniqueMethodWithName("joinOfInt");
- assertThat(joinOfInt, not(isPresent()));
- // API entry is kept, hence the presence of Metadata.
- AnnotationSubject annotationSubject = util.annotation(METADATA_TYPE);
- assertThat(annotationSubject, isPresent());
- // TODO(b/70169921): need further inspection.
-
- ClassSubject signed = inspector.clazz(signedClassName);
- assertThat(signed, isRenamed());
- commaJoinOfInt = signed.uniqueMethodWithName("commaSeparatedJoinOfInt");
- assertThat(commaJoinOfInt, isPresent());
- assertThat(commaJoinOfInt, not(isRenamed()));
- joinOfInt = signed.uniqueMethodWithName("joinOfInt");
- assertThat(joinOfInt, isRenamed());
- // API entry is kept, hence the presence of Metadata.
- annotationSubject = util.annotation(METADATA_TYPE);
- assertThat(annotationSubject, isPresent());
- // TODO(b/70169921): need further inspection.
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/multifileclass_app";
- ProcessResult kotlinTestCompileResult =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- // TODO(b/143687784): update to just .compile() once fixed.
- .compileRaw();
- // TODO(b/70169921): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
- }
-
- @Test
- public void testMetadataInMultifileClass_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(multifileLibJar)
- // Keep UtilKt#comma*Join*().
- .addKeepRules("-keep class **.UtilKt")
- .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
- // Keep yet rename joinOf*(String).
- .addKeepRules(
- "-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String utilClassName = pkg + ".multifileclass_lib.UtilKt";
- final String signedClassName = pkg + ".multifileclass_lib.UtilKt__SignedKt";
- compileResult.inspect(inspector -> {
- ClassSubject util = inspector.clazz(utilClassName);
- assertThat(util, isPresent());
- assertThat(util, not(isRenamed()));
- MethodSubject commaJoinOfInt = util.uniqueMethodWithName("commaSeparatedJoinOfInt");
- assertThat(commaJoinOfInt, isPresent());
- assertThat(commaJoinOfInt, not(isRenamed()));
- MethodSubject joinOfInt = util.uniqueMethodWithName("joinOfInt");
- assertThat(joinOfInt, isPresent());
- assertThat(joinOfInt, isRenamed());
- // API entry is kept, hence the presence of Metadata.
- AnnotationSubject annotationSubject = util.annotation(METADATA_TYPE);
- assertThat(annotationSubject, isPresent());
- // TODO(b/70169921): need further inspection.
-
- ClassSubject signed = inspector.clazz(signedClassName);
- assertThat(signed, isRenamed());
- commaJoinOfInt = signed.uniqueMethodWithName("commaSeparatedJoinOfInt");
- assertThat(commaJoinOfInt, isPresent());
- assertThat(commaJoinOfInt, not(isRenamed()));
- joinOfInt = signed.uniqueMethodWithName("joinOfInt");
- assertThat(joinOfInt, isRenamed());
- // API entry is kept, hence the presence of Metadata.
- annotationSubject = util.annotation(METADATA_TYPE);
- assertThat(annotationSubject, isPresent());
- // TODO(b/70169921): need further inspection.
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/multifileclass_app";
- ProcessResult kotlinTestCompileResult =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- // TODO(b/143687784): update to just .compile() once fixed.
- .compileRaw();
- // TODO(b/70169921): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
deleted file mode 100644
index d75ec09..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) 2019, 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInParameterTypeTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInParameterTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path parameterTypeLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String parameterTypeLibFolder = PKG_PREFIX + "/parametertype_lib";
- parameterTypeLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(parameterTypeLibFolder, "lib"))
- .compile();
- }
-
- @Test
- public void testMetadataInParameterType_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(parameterTypeLibJar)
- // Keep non-private members of Impl
- .addKeepRules("-keep public class **.Impl { !private *; }")
- // Keep Itf, but allow minification.
- .addKeepRules("-keep,allowobfuscation class **.Itf")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String itfClassName = pkg + ".parametertype_lib.Itf";
- final String implClassName = pkg + ".parametertype_lib.Impl";
- compileResult.inspect(inspector -> {
- ClassSubject itf = inspector.clazz(itfClassName);
- assertThat(itf, isRenamed());
-
- ClassSubject impl = inspector.clazz(implClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Itf")));
- assertTrue(superTypes.stream().anyMatch(
- supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
- List<ClassSubject> parameterTypes = kmClass.getParameterTypesInFunctions();
- assertTrue(parameterTypes.stream().anyMatch(
- parameterType -> parameterType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/parametertype_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".parametertype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::bar", "Program::bar");
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
deleted file mode 100644
index 556780c..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInPropertyTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInPropertyTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path propertyTypeLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String propertyTypeLibFolder = PKG_PREFIX + "/fragile_property_lib";
- propertyTypeLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib"))
- .compile();
- }
-
- @Test
- public void testMetadataInProperty_getterOnly() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(propertyTypeLibJar)
- // Keep property getters
- .addKeepRules("-keep class **.Person { <init>(...); }")
- .addKeepRules("-keepclassmembers class **.Person { *** get*(); }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String personClassName = pkg + ".fragile_property_lib.Person";
- compileResult.inspect(inspector -> {
- ClassSubject person = inspector.clazz(personClassName);
- assertThat(person, isPresent());
- assertThat(person, not(isRenamed()));
-
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = person.getKmClass();
- assertThat(kmClass, isPresent());
-
- KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name");
- assertThat(name, isPresent());
- assertThat(name, not(isExtensionProperty()));
- assertNotNull(name.fieldSignature());
- assertNotNull(name.getterSignature());
- assertNull(name.setterSignature());
-
- KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
- assertThat(familyName, isPresent());
- assertThat(familyName, not(isExtensionProperty()));
- // No backing field for property `familyName`
- assertNull(familyName.fieldSignature());
- assertNotNull(familyName.getterSignature());
- // No setter for property `familyName`
- assertNull(familyName.setterSignature());
-
- KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
- assertThat(age, isPresent());
- assertThat(age, not(isExtensionProperty()));
- assertNotNull(age.fieldSignature());
- assertNotNull(age.getterSignature());
- assertNull(name.setterSignature());
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/fragile_property_only_getter";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "getter_user"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".fragile_property_only_getter.Getter_userKt")
- .assertSuccessWithOutputLines("true", "false", "Hey");
- }
-
- @Test
- public void testMetadataInProperty_setterOnly() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(propertyTypeLibJar)
- // Keep property setters (and users)
- .addKeepRules("-keep class **.Person { <init>(...); }")
- .addKeepRules("-keepclassmembers class **.Person { void set*(...); }")
- .addKeepRules("-keepclassmembers class **.Person { void aging(); }")
- .addKeepRules("-keepclassmembers class **.Person { void change*(...); }")
- // Keep LibKt extension methods
- .addKeepRules("-keep class **.LibKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String personClassName = pkg + ".fragile_property_lib.Person";
- compileResult.inspect(inspector -> {
- ClassSubject person = inspector.clazz(personClassName);
- assertThat(person, isPresent());
- assertThat(person, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = person.getKmClass();
- assertThat(kmClass, isPresent());
-
- KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name");
- assertThat(name, isPresent());
- assertThat(name, not(isExtensionProperty()));
- assertNull(name.fieldSignature());
- assertNull(name.getterSignature());
- assertNotNull(name.setterSignature());
-
- KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
- assertThat(familyName, not(isPresent()));
-
- KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
- assertThat(age, isPresent());
- assertThat(age, not(isExtensionProperty()));
- assertNotNull(age.fieldSignature());
- assertNull(age.getterSignature());
- assertNotNull(age.setterSignature());
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/fragile_property_only_setter";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "setter_user"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".fragile_property_only_setter.Setter_userKt")
- .assertSuccessWithOutputLines();
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
deleted file mode 100644
index 78a1514..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2019, 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInPropertyTypeTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInPropertyTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path propertyTypeLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String propertyTypeLibFolder = PKG_PREFIX + "/propertytype_lib";
- propertyTypeLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib"))
- .compile();
- }
-
- @Test
- public void testMetadataInProperty_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(propertyTypeLibJar)
- // Keep non-private members of Impl
- .addKeepRules("-keep public class **.Impl { !private *; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String itfClassName = pkg + ".propertytype_lib.Itf";
- final String implClassName = pkg + ".propertytype_lib.Impl";
- compileResult.inspect(inspector -> {
- ClassSubject itf = inspector.clazz(itfClassName);
- assertThat(itf, isRenamed());
-
- ClassSubject impl = inspector.clazz(implClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Itf")));
- assertTrue(superTypes.stream().anyMatch(
- supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
- List<ClassSubject> propertyReturnTypes = kmClass.getReturnTypesInProperties();
- assertTrue(propertyReturnTypes.stream().anyMatch(
- propertyType -> propertyType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/propertytype_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".propertytype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::8");
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
deleted file mode 100644
index 4f24442..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) 2019, 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInReturnTypeTest extends KotlinMetadataTestBase {
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInReturnTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path returnTypeLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String returnTypeLibFolder = PKG_PREFIX + "/returntype_lib";
- returnTypeLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(returnTypeLibFolder, "lib"))
- .compile();
- }
-
- @Test
- public void testMetadataInReturnType_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(returnTypeLibJar)
- // Keep non-private members of Impl
- .addKeepRules("-keep public class **.Impl { !private *; }")
- // Keep Itf, but allow minification.
- .addKeepRules("-keep,allowobfuscation class **.Itf")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String itfClassName = pkg + ".returntype_lib.Itf";
- final String implClassName = pkg + ".returntype_lib.Impl";
- compileResult.inspect(inspector -> {
- ClassSubject itf = inspector.clazz(itfClassName);
- assertThat(itf, isRenamed());
-
- ClassSubject impl = inspector.clazz(implClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Itf")));
- assertTrue(superTypes.stream().anyMatch(
- supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
- List<ClassSubject> functionReturnTypes = kmClass.getReturnTypesInFunctions();
- assertTrue(functionReturnTypes.stream().anyMatch(
- returnType -> returnType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/returntype_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".returntype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo", "Program::foo", "true");
- }
-}
-
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java
deleted file mode 100644
index a2d4fa1..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInSealedClassTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInSealedClassTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path sealedLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String sealedLibFolder = PKG_PREFIX + "/sealed_lib";
- sealedLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(getKotlinFileInTest(sealedLibFolder, "lib"))
- .compile();
- }
-
- @Test
- public void testMetadataInSealedClass_valid() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(sealedLibJar)
- // Keep the Expr class
- .addKeepRules("-keep class **.Expr")
- // Keep the extension function
- .addKeepRules("-keep class **.LibKt { <methods>; }")
- // Keep the factory object and utils
- .addKeepRules("-keep class **.ExprFactory { *; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String numClassName = pkg + ".sealed_lib.Num";
- final String exprClassName = pkg + ".sealed_lib.Expr";
- final String libClassName = pkg + ".sealed_lib.LibKt";
- compileResult.inspect(inspector -> {
- ClassSubject num = inspector.clazz(numClassName);
- assertThat(num, isRenamed());
-
- ClassSubject expr = inspector.clazz(exprClassName);
- assertThat(expr, isPresent());
- assertThat(expr, not(isRenamed()));
-
- KmClassSubject kmClass = expr.getKmClass();
- assertThat(kmClass, isPresent());
-
- kmClass.getSealedSubclassDescriptors().forEach(sealedSubclassDescriptor -> {
- ClassSubject sealedSubclass =
- inspector.clazz(descriptorToJavaType(sealedSubclassDescriptor));
- assertThat(sealedSubclass, isRenamed());
- assertEquals(sealedSubclassDescriptor, sealedSubclass.getFinalDescriptor());
- });
-
- ClassSubject libKt = inspector.clazz(libClassName);
- assertThat(expr, isPresent());
- assertThat(expr, not(isRenamed()));
-
- KmPackageSubject kmPackage = libKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject eval = kmPackage.kmFunctionExtensionWithUniqueName("eval");
- assertThat(eval, isPresent());
- assertThat(eval, isExtensionFunction());
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/sealed_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "valid"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".sealed_app.ValidKt")
- .assertSuccessWithOutputLines("6");
- }
-
- @Test
- public void testMetadataInSealedClass_invalid() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(sealedLibJar)
- // Keep the Expr class
- .addKeepRules("-keep class **.Expr")
- // Keep the extension function
- .addKeepRules("-keep class **.LibKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String exprClassName = pkg + ".sealed_lib.Expr";
- final String numClassName = pkg + ".sealed_lib.Num";
- final String libClassName = pkg + ".sealed_lib.LibKt";
- compileResult.inspect(inspector -> {
- // Without any specific keep rule and no instantiation point, it's not necessary to keep
- // sub classes of Expr.
- ClassSubject num = inspector.clazz(numClassName);
- assertThat(num, not(isPresent()));
-
- ClassSubject expr = inspector.clazz(exprClassName);
- assertThat(expr, isPresent());
- assertThat(expr, not(isRenamed()));
-
- KmClassSubject kmClass = expr.getKmClass();
- assertThat(kmClass, isPresent());
-
- assertTrue(kmClass.getSealedSubclassDescriptors().isEmpty());
-
- ClassSubject libKt = inspector.clazz(libClassName);
- assertThat(expr, isPresent());
- assertThat(expr, not(isRenamed()));
-
- KmPackageSubject kmPackage = libKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject eval = kmPackage.kmFunctionExtensionWithUniqueName("eval");
- assertThat(eval, isPresent());
- assertThat(eval, isExtensionFunction());
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/sealed_app";
- ProcessResult kotlinTestCompileResult =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "invalid"))
- .setOutputPath(temp.newFolder().toPath())
- .compileRaw();
-
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(kotlinTestCompileResult.stderr, containsString("cannot access"));
- assertThat(kotlinTestCompileResult.stderr, containsString("private in 'Expr'"));
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSuperTypeTest.java
deleted file mode 100644
index 722004a..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSuperTypeTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (c) 2019, 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInSuperTypeTest extends KotlinMetadataTestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0} target: {1}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
- }
-
- public MetadataRenameInSuperTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
- super(targetVersion);
- this.parameters = parameters;
- }
-
- private static Path superTypeLibJar;
-
- @BeforeClass
- public static void createLibJar() throws Exception {
- String superTypeLibFolder = PKG_PREFIX + "/supertype_lib";
- superTypeLibJar =
- kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
- .addSourceFiles(
- getKotlinFileInTest(superTypeLibFolder, "impl"),
- getKotlinFileInTest(superTypeLibFolder + "/internal", "itf"))
- .compile();
- }
-
- @Test
- public void b143687784_merged() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(superTypeLibJar)
- // Keep non-private members except for ones in `internal` definitions.
- .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String itfClassName = pkg + ".supertype_lib.internal.Itf";
- final String implClassName = pkg + ".supertype_lib.Impl";
- compileResult.inspect(inspector -> {
- assertThat(inspector.clazz(itfClassName), not(isPresent()));
-
- ClassSubject impl = inspector.clazz(implClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("internal")));
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Itf")));
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/supertype_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".supertype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
- }
-
- @Test
- public void b143687784_renamed() throws Exception {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramFiles(superTypeLibJar)
- // Keep non-private members except for ones in `internal` definitions.
- .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
- // Keep `internal` definitions, but allow minification.
- .addKeepRules("-keep,allowobfuscation class **.internal.** { *; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile();
- String pkg = getClass().getPackage().getName();
- final String itfClassName = pkg + ".supertype_lib.internal.Itf";
- final String implClassName = pkg + ".supertype_lib.Impl";
- compileResult.inspect(inspector -> {
- ClassSubject itf = inspector.clazz(itfClassName);
- assertThat(itf, isRenamed());
-
- ClassSubject impl = inspector.clazz(implClassName);
- assertThat(impl, isPresent());
- assertThat(impl, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = impl.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("internal")));
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Itf")));
- assertTrue(superTypes.stream().anyMatch(
- supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
- });
-
- Path libJar = compileResult.writeToZip();
-
- String appFolder = PKG_PREFIX + "/supertype_app";
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), pkg + ".supertype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
new file mode 100644
index 0000000..d03fcd1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -0,0 +1,199 @@
+// Copyright (c) 2019, 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInClasspathTypeTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInClasspathTypeTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> baseLibJarMap = new HashMap<>();
+ private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String baseLibFolder = PKG_PREFIX + "/classpath_lib_base";
+ String extLibFolder = PKG_PREFIX + "/classpath_lib_ext";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path baseLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(baseLibFolder, "itf"))
+ .compile();
+ Path extLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addClasspathFiles(baseLibJar)
+ .addSourceFiles(getKotlinFileInTest(extLibFolder, "impl"))
+ .compile();
+ baseLibJarMap.put(targetVersion, baseLibJar);
+ extLibJarMap.put(targetVersion, extLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInClasspathType_merged() throws Exception {
+ Path baseLibJar = baseLibJarMap.get(targetVersion);
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(baseLibJar)
+ .addProgramFiles(extLibJarMap.get(targetVersion))
+ // Keep the Extra class and its interface (which has the method).
+ .addKeepRules("-keep class **.Extra")
+ // Keep the ImplKt extension method which requires metadata
+ // to be called with Kotlin syntax from other kotlin code.
+ .addKeepRules("-keep class **.ImplKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectMerged)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(baseLibJar, libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
+ .assertSuccessWithOutputLines("Impl::foo");
+ }
+
+ private void inspectMerged(CodeInspector inspector) {
+ String implClassName = PKG + ".classpath_lib_ext.Impl";
+ String implKtClassName = PKG + ".classpath_lib_ext.ImplKt";
+ String extraClassName = PKG + ".classpath_lib_ext.Extra";
+
+ assertThat(inspector.clazz(implClassName), not(isPresent()));
+
+ ClassSubject implKt = inspector.clazz(implKtClassName);
+ assertThat(implKt, isPresent());
+ assertThat(implKt, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = implKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
+ assertThat(kmFunction, isPresent());
+
+ ClassSubject extra = inspector.clazz(extraClassName);
+ assertThat(extra, isPresent());
+ assertThat(extra, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = extra.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Impl")));
+ // Can't build ClassSubject with Itf in classpath. Instead, check if the reference to Itf is
+ // not altered via descriptors.
+ List<String> superTypeDescriptors = kmClass.getSuperTypeDescriptors();
+ assertTrue(superTypeDescriptors.stream().noneMatch(supertype -> supertype.contains("Impl")));
+ assertTrue(superTypeDescriptors.stream().anyMatch(supertype -> supertype.contains("Itf")));
+ }
+
+ @Test
+ public void testMetadataInClasspathType_renamed() throws Exception {
+ Path baseLibJar = baseLibJarMap.get(targetVersion);
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(baseLibJar)
+ .addProgramFiles(extLibJarMap.get(targetVersion))
+ // Keep the Extra class and its interface (which has the method).
+ .addKeepRules("-keep class **.Extra")
+ // Keep Super, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.Impl")
+ // Keep the ImplKt extension method which requires metadata
+ // to be called with Kotlin syntax from other kotlin code.
+ .addKeepRules("-keep class **.ImplKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectRenamed)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(baseLibJar, libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
+ .assertSuccessWithOutputLines("Impl::foo");
+ }
+
+ private void inspectRenamed(CodeInspector inspector) {
+ String implClassName = PKG + ".classpath_lib_ext.Impl";
+ String implKtClassName = PKG + ".classpath_lib_ext.ImplKt";
+ String extraClassName = PKG + ".classpath_lib_ext.Extra";
+
+ ClassSubject impl = inspector.clazz(implClassName);
+ assertThat(impl, isRenamed());
+
+ ClassSubject implKt = inspector.clazz(implKtClassName);
+ assertThat(implKt, isPresent());
+ assertThat(implKt, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = implKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
+ assertThat(kmFunction, isExtensionFunction());
+
+ ClassSubject extra = inspector.clazz(extraClassName);
+ assertThat(extra, isPresent());
+ assertThat(extra, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = extra.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Impl")));
+ assertTrue(superTypes.stream().anyMatch(
+ supertype -> supertype.getFinalDescriptor().equals(impl.getFinalDescriptor())));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
new file mode 100644
index 0000000..5059a1e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -0,0 +1,131 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInCompanionTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInCompanionTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static Map<KotlinTargetVersion, Path> companionLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String companionLibFolder = PKG_PREFIX + "/companion_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path companionLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(companionLibFolder, "lib"))
+ .compile();
+ companionLibJarMap.put(targetVersion, companionLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInCompanion_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(companionLibJarMap.get(targetVersion))
+ // Keep the B class and its interface (which has the doStuff method).
+ .addKeepRules("-keep class **.B")
+ .addKeepRules("-keep class **.I { <methods>; }")
+ // Keep getters for B$Companion.(singleton|foo) which will be referenced at the app.
+ .addKeepRules("-keepclassmembers class **.B$* { *** get*(...); }")
+ // No rule for Super, but will be kept and renamed.
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspect)
+ .writeToZip();
+
+ ProcessResult kotlinTestCompileResult =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ // TODO(b/70169921): update to just .compile() once fixed.
+ .compileRaw();
+
+ // TODO(b/70169921): should be able to compile!
+ assertNotEquals(0, kotlinTestCompileResult.exitCode);
+ assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: singleton"));
+ assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: foo"));
+ }
+
+ private void inspect(CodeInspector inspector) {
+ final String superClassName = PKG + ".companion_lib.Super";
+ final String bClassName = PKG + ".companion_lib.B";
+ final String companionClassName = PKG + ".companion_lib.B$Companion";
+
+ ClassSubject sup = inspector.clazz(superClassName);
+ assertThat(sup, isRenamed());
+
+ ClassSubject impl = inspector.clazz(bClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Super")));
+ assertTrue(superTypes.stream().anyMatch(
+ supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
+
+ // Bridge for the property in the companion that needs a backing field.
+ MethodSubject singletonBridge = impl.uniqueMethodWithName("access$getSingleton$cp");
+ assertThat(singletonBridge, isRenamed());
+
+ // For B$Companion.foo, no backing field needed, hence no bridge.
+ MethodSubject fooBridge = impl.uniqueMethodWithName("access$getFoo$cp");
+ assertThat(fooBridge, not(isPresent()));
+
+ ClassSubject companion = inspector.clazz(companionClassName);
+ assertThat(companion, isRenamed());
+
+ MethodSubject singletonGetter = companion.uniqueMethodWithName("getSingleton");
+ assertThat(singletonGetter, isPresent());
+
+ MethodSubject fooGetter = companion.uniqueMethodWithName("getFoo");
+ assertThat(fooGetter, isPresent());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
new file mode 100644
index 0000000..917ede7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -0,0 +1,185 @@
+// Copyright (c) 2019, 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInExtensionFunctionTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInExtensionFunctionTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String extLibFolder = PKG_PREFIX + "/extension_function_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path extLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(extLibFolder, "B"))
+ .compile();
+ extLibJarMap.put(targetVersion, extLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInExtensionFunction_merged() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(extLibJarMap.get(targetVersion))
+ // Keep the B class and its interface (which has the doStuff method).
+ .addKeepRules("-keep class **.B")
+ .addKeepRules("-keep class **.I { <methods>; }")
+ // Keep the BKt extension function which requires metadata
+ // to be called with Kotlin syntax from other kotlin code.
+ .addKeepRules("-keep class **.BKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectMerged)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
+ .assertSuccessWithOutputLines("do stuff", "do stuff");
+ }
+
+ private void inspectMerged(CodeInspector inspector) {
+ String superClassName = PKG + ".extension_function_lib.Super";
+ String bClassName = PKG + ".extension_function_lib.B";
+ String bKtClassName = PKG + ".extension_function_lib.BKt";
+
+ assertThat(inspector.clazz(superClassName), not(isPresent()));
+
+ ClassSubject impl = inspector.clazz(bClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Super")));
+
+ ClassSubject bKt = inspector.clazz(bKtClassName);
+ assertThat(bKt, isPresent());
+ assertThat(bKt, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = bKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
+ assertThat(kmFunction, isExtensionFunction());
+ }
+
+ @Test
+ public void testMetadataInExtensionFunction_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(extLibJarMap.get(targetVersion))
+ // Keep the B class and its interface (which has the doStuff method).
+ .addKeepRules("-keep class **.B")
+ .addKeepRules("-keep class **.I { <methods>; }")
+ // Keep Super, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.Super")
+ // Keep the BKt extension function which requires metadata
+ // to be called with Kotlin syntax from other kotlin code.
+ .addKeepRules("-keep class **.BKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectRenamed)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
+ .assertSuccessWithOutputLines("do stuff", "do stuff");
+ }
+
+ private void inspectRenamed(CodeInspector inspector) {
+ String superClassName = PKG + ".extension_function_lib.Super";
+ String bClassName = PKG + ".extension_function_lib.B";
+ String bKtClassName = PKG + ".extension_function_lib.BKt";
+
+ ClassSubject sup = inspector.clazz(superClassName);
+ assertThat(sup, isPresent());
+ assertThat(sup, isRenamed());
+
+ ClassSubject impl = inspector.clazz(bClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Super")));
+ assertTrue(superTypes.stream().anyMatch(
+ supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
+
+ ClassSubject bKt = inspector.clazz(bKtClassName);
+ assertThat(bKt, isPresent());
+ assertThat(bKt, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = bKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
+ assertThat(kmFunction, isExtensionFunction());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
new file mode 100644
index 0000000..5fb5150
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -0,0 +1,192 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInExtensionPropertyTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInExtensionPropertyTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String extLibFolder = PKG_PREFIX + "/extension_property_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path extLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(extLibFolder, "B"))
+ .compile();
+ extLibJarMap.put(targetVersion, extLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInExtensionProperty_merged() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(extLibJarMap.get(targetVersion))
+ // Keep the B class and its interface (which has the doStuff method).
+ .addKeepRules("-keep class **.B")
+ .addKeepRules("-keep class **.I { <methods>; }")
+ // Keep the BKt extension property which requires metadata
+ // to be called with Kotlin syntax from other kotlin code.
+ .addKeepRules("-keep class **.BKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectMerged)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
+ .assertSuccessWithOutputLines("do stuff", "do stuff");
+ }
+
+ private void inspectMerged(CodeInspector inspector) {
+ String superClassName = PKG + ".extension_property_lib.Super";
+ String bClassName = PKG + ".extension_property_lib.B";
+ String bKtClassName = PKG + ".extension_property_lib.BKt";
+
+ assertThat(inspector.clazz(superClassName), not(isPresent()));
+
+ ClassSubject impl = inspector.clazz(bClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Super")));
+ KmFunctionSubject kmFunction = kmClass.kmFunctionWithUniqueName("doStuff");
+ assertThat(kmFunction, isPresent());
+ assertThat(kmFunction, not(isExtensionFunction()));
+
+ ClassSubject bKt = inspector.clazz(bKtClassName);
+ assertThat(bKt, isPresent());
+ assertThat(bKt, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = bKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI");
+ assertThat(kmProperty, isExtensionProperty());
+ assertNotNull(kmProperty.getterSignature());
+ }
+
+ @Test
+ public void testMetadataInExtensionProperty_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(extLibJarMap.get(targetVersion))
+ // Keep the B class and its interface (which has the doStuff method).
+ .addKeepRules("-keep class **.B")
+ .addKeepRules("-keep class **.I { <methods>; }")
+ // Keep Super, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.Super")
+ // Keep the BKt extension property which requires metadata
+ // to be called with Kotlin syntax from other kotlin code.
+ .addKeepRules("-keep class **.BKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectRenamed)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
+ .assertSuccessWithOutputLines("do stuff", "do stuff");
+ }
+
+ private void inspectRenamed(CodeInspector inspector) {
+ String superClassName = PKG + ".extension_property_lib.Super";
+ String bClassName = PKG + ".extension_property_lib.B";
+ String bKtClassName = PKG + ".extension_property_lib.BKt";
+
+ ClassSubject sup = inspector.clazz(superClassName);
+ assertThat(sup, isRenamed());
+
+ ClassSubject impl = inspector.clazz(bClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Super")));
+ assertTrue(superTypes.stream().anyMatch(
+ supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
+
+ ClassSubject bKt = inspector.clazz(bKtClassName);
+ assertThat(bKt, isPresent());
+ assertThat(bKt, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = bKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI");
+ assertThat(kmProperty, isExtensionProperty());
+ assertNotNull(kmProperty.getterSignature());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
new file mode 100644
index 0000000..f6649b7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -0,0 +1,184 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInFunctionTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInFunctionTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> funLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String funLibFolder = PKG_PREFIX + "/function_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path funLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(funLibFolder, "B"))
+ .compile();
+ funLibJarMap.put(targetVersion, funLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInFunction_merged() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(funLibJarMap.get(targetVersion))
+ // Keep the B class and its interface (which has the doStuff method).
+ .addKeepRules("-keep class **.B")
+ .addKeepRules("-keep class **.I { <methods>; }")
+ // Keep the BKt method, which will be called from other kotlin code.
+ .addKeepRules("-keep class **.BKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectMerged)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".function_app.MainKt")
+ .assertSuccessWithOutputLines("do stuff", "do stuff");
+ }
+
+ private void inspectMerged(CodeInspector inspector) {
+ String superClassName = PKG + ".function_lib.Super";
+ String bClassName = PKG + ".function_lib.B";
+ String bKtClassName = PKG + ".function_lib.BKt";
+
+ assertThat(inspector.clazz(superClassName), not(isPresent()));
+
+ ClassSubject impl = inspector.clazz(bClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Super")));
+
+ ClassSubject bKt = inspector.clazz(bKtClassName);
+ assertThat(bKt, isPresent());
+ assertThat(bKt, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = bKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun");
+ assertThat(kmFunction, isPresent());
+ assertThat(kmFunction, not(isExtensionFunction()));
+ }
+
+ @Test
+ public void testMetadataInFunction_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(funLibJarMap.get(targetVersion))
+ // Keep the B class and its interface (which has the doStuff method).
+ .addKeepRules("-keep class **.B")
+ .addKeepRules("-keep class **.I { <methods>; }")
+ // Keep Super, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.Super")
+ // Keep the BKt method, which will be called from other kotlin code.
+ .addKeepRules("-keep class **.BKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectRenamed)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".function_app.MainKt")
+ .assertSuccessWithOutputLines("do stuff", "do stuff");
+ }
+
+ private void inspectRenamed(CodeInspector inspector) {
+ String superClassName = PKG + ".function_lib.Super";
+ String bClassName = PKG + ".function_lib.B";
+ String bKtClassName = PKG + ".function_lib.BKt";
+
+ ClassSubject sup = inspector.clazz(superClassName);
+ assertThat(sup, isRenamed());
+
+ ClassSubject impl = inspector.clazz(bClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Super")));
+ assertTrue(superTypes.stream().anyMatch(
+ supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
+
+ ClassSubject bKt = inspector.clazz(bKtClassName);
+ assertThat(bKt, isPresent());
+ assertThat(bKt, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = bKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun");
+ assertThat(kmFunction, isPresent());
+ assertThat(kmFunction, not(isExtensionFunction()));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
new file mode 100644
index 0000000..ba6b08f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInLibraryTypeTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInLibraryTypeTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> baseLibJarMap = new HashMap<>();
+ private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>();
+ private static final Map<KotlinTargetVersion, Path> appJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String baseLibFolder = PKG_PREFIX + "/libtype_lib_base";
+ String extLibFolder = PKG_PREFIX + "/libtype_lib_ext";
+ String appFolder = PKG_PREFIX + "/libtype_app";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path baseLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(baseLibFolder, "base"))
+ .compile();
+ Path extLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addClasspathFiles(baseLibJar)
+ .addSourceFiles(getKotlinFileInTest(extLibFolder, "ext"))
+ .compile();
+ Path appJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addClasspathFiles(baseLibJar)
+ .addClasspathFiles(extLibJar)
+ .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+ .compile();
+ baseLibJarMap.put(targetVersion, baseLibJar);
+ extLibJarMap.put(targetVersion, extLibJar);
+ appJarMap.put(targetVersion, appJar);
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ String main = PKG + ".libtype_app.MainKt";
+ Path out =
+ testForR8(parameters.getBackend())
+ // Intentionally not providing basLibJar as lib file nor classpath file.
+ .addProgramFiles(extLibJarMap.get(targetVersion), appJarMap.get(targetVersion))
+ // Keep Ext extension method which requires metadata to be called with Kotlin syntax
+ // from other kotlin code.
+ .addKeepRules("-keep class **.ExtKt { <methods>; }")
+ // Keep the main entry.
+ .addKeepMainRule(main)
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .allowDiagnosticWarningMessages()
+ // -dontoptimize so that basic code structure is kept.
+ .noOptimization()
+ .compile()
+ .inspect(this::inspect)
+ .assertAllWarningMessagesMatch(
+ anyOf(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
+ equalTo("Resource 'META-INF/main.kotlin_module' already exists.")))
+ .writeToZip();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJarMap.get(targetVersion))
+ .addClasspath(out)
+ .run(parameters.getRuntime(), main)
+ .assertSuccessWithOutputLines("Sub::foo", "Sub::boo", "true");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ String extClassName = PKG + ".libtype_lib_ext.ExtKt";
+ ClassSubject ext = inspector.clazz(extClassName);
+ assertThat(ext, isPresent());
+ assertThat(ext, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = ext.getKmPackage();
+ assertThat(kmPackage, isPresent());
+ // Type appearance of library type, Base, should be kept, even if it's not provided.
+ // Note that the resulting ClassSubject for Base is an absent one as we don't provide it, and
+ // thus we can't use `getReturnTypesInFunctions`, which filters out absent class subject.
+ assertTrue(kmPackage.getReturnTypeDescriptorsInFunctions().stream().anyMatch(
+ returnType -> returnType.contains("Base")));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
new file mode 100644
index 0000000..2dc57bf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
@@ -0,0 +1,177 @@
+// Copyright (c) 2019, 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInMultifileClassTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInMultifileClassTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> multifileLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String multifileLibFolder = PKG_PREFIX + "/multifileclass_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path multifileLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(
+ getKotlinFileInTest(multifileLibFolder, "signed"),
+ getKotlinFileInTest(multifileLibFolder, "unsigned"))
+ .compile();
+ multifileLibJarMap.put(targetVersion, multifileLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInMultifileClass_merged() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(multifileLibJarMap.get(targetVersion))
+ // Keep UtilKt#comma*Join*(). Let R8 optimize (inline) others, such as joinOf*(String).
+ .addKeepRules("-keep class **.UtilKt")
+ .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectMerged)
+ .writeToZip();
+
+ ProcessResult kotlinTestCompileResult =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ // TODO(b/70169921): update to just .compile() once fixed.
+ .compileRaw();
+ // TODO(b/70169921): should be able to compile!
+ assertNotEquals(0, kotlinTestCompileResult.exitCode);
+ assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
+ }
+
+ private void inspectMerged(CodeInspector inspector) {
+ String utilClassName = PKG + ".multifileclass_lib.UtilKt";
+ String signedClassName = PKG + ".multifileclass_lib.UtilKt__SignedKt";
+
+ ClassSubject util = inspector.clazz(utilClassName);
+ assertThat(util, isPresent());
+ assertThat(util, not(isRenamed()));
+ MethodSubject commaJoinOfInt = util.uniqueMethodWithName("commaSeparatedJoinOfInt");
+ assertThat(commaJoinOfInt, isPresent());
+ assertThat(commaJoinOfInt, not(isRenamed()));
+ MethodSubject joinOfInt = util.uniqueMethodWithName("joinOfInt");
+ assertThat(joinOfInt, not(isPresent()));
+ // API entry is kept, hence the presence of Metadata.
+ AnnotationSubject annotationSubject = util.annotation(METADATA_TYPE);
+ assertThat(annotationSubject, isPresent());
+ // TODO(b/70169921): need further inspection.
+
+ ClassSubject signed = inspector.clazz(signedClassName);
+ assertThat(signed, isRenamed());
+ commaJoinOfInt = signed.uniqueMethodWithName("commaSeparatedJoinOfInt");
+ assertThat(commaJoinOfInt, isPresent());
+ assertThat(commaJoinOfInt, not(isRenamed()));
+ joinOfInt = signed.uniqueMethodWithName("joinOfInt");
+ assertThat(joinOfInt, isRenamed());
+ // API entry is kept, hence the presence of Metadata.
+ annotationSubject = util.annotation(METADATA_TYPE);
+ assertThat(annotationSubject, isPresent());
+ // TODO(b/70169921): need further inspection.
+ }
+
+ @Test
+ public void testMetadataInMultifileClass_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(multifileLibJarMap.get(targetVersion))
+ // Keep UtilKt#comma*Join*().
+ .addKeepRules("-keep class **.UtilKt")
+ .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
+ // Keep yet rename joinOf*(String).
+ .addKeepRules(
+ "-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectRenamed)
+ .writeToZip();
+
+ ProcessResult kotlinTestCompileResult =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ // TODO(b/70169921): update to just .compile() once fixed.
+ .compileRaw();
+ // TODO(b/70169921): should be able to compile!
+ assertNotEquals(0, kotlinTestCompileResult.exitCode);
+ assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
+ }
+
+ private void inspectRenamed(CodeInspector inspector) {
+ String utilClassName = PKG + ".multifileclass_lib.UtilKt";
+ String signedClassName = PKG + ".multifileclass_lib.UtilKt__SignedKt";
+
+ ClassSubject util = inspector.clazz(utilClassName);
+ assertThat(util, isPresent());
+ assertThat(util, not(isRenamed()));
+ MethodSubject commaJoinOfInt = util.uniqueMethodWithName("commaSeparatedJoinOfInt");
+ assertThat(commaJoinOfInt, isPresent());
+ assertThat(commaJoinOfInt, not(isRenamed()));
+ MethodSubject joinOfInt = util.uniqueMethodWithName("joinOfInt");
+ assertThat(joinOfInt, isPresent());
+ assertThat(joinOfInt, isRenamed());
+ // API entry is kept, hence the presence of Metadata.
+ AnnotationSubject annotationSubject = util.annotation(METADATA_TYPE);
+ assertThat(annotationSubject, isPresent());
+ // TODO(b/70169921): need further inspection.
+
+ ClassSubject signed = inspector.clazz(signedClassName);
+ assertThat(signed, isRenamed());
+ commaJoinOfInt = signed.uniqueMethodWithName("commaSeparatedJoinOfInt");
+ assertThat(commaJoinOfInt, isPresent());
+ assertThat(commaJoinOfInt, not(isRenamed()));
+ joinOfInt = signed.uniqueMethodWithName("joinOfInt");
+ assertThat(joinOfInt, isRenamed());
+ // API entry is kept, hence the presence of Metadata.
+ annotationSubject = util.annotation(METADATA_TYPE);
+ assertThat(annotationSubject, isPresent());
+ // TODO(b/70169921): need further inspection.
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
new file mode 100644
index 0000000..6dcbc1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2019, 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInParameterTypeTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInParameterTypeTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> parameterTypeLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String parameterTypeLibFolder = PKG_PREFIX + "/parametertype_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path parameterTypeLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(parameterTypeLibFolder, "lib"))
+ .compile();
+ parameterTypeLibJarMap.put(targetVersion, parameterTypeLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInParameterType_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(parameterTypeLibJarMap.get(targetVersion))
+ // Keep non-private members of Impl
+ .addKeepRules("-keep public class **.Impl { !private *; }")
+ // Keep Itf, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.Itf")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspect)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/parametertype_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".parametertype_app.MainKt")
+ .assertSuccessWithOutputLines("Impl::bar", "Program::bar");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ String itfClassName = PKG + ".parametertype_lib.Itf";
+ String implClassName = PKG + ".parametertype_lib.Impl";
+
+ ClassSubject itf = inspector.clazz(itfClassName);
+ assertThat(itf, isRenamed());
+
+ ClassSubject impl = inspector.clazz(implClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Itf")));
+ assertTrue(superTypes.stream().anyMatch(
+ supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+ List<ClassSubject> parameterTypes = kmClass.getParameterTypesInFunctions();
+ assertTrue(parameterTypes.stream().anyMatch(
+ parameterType -> parameterType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
new file mode 100644
index 0000000..d8b230d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -0,0 +1,183 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInPropertyTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInPropertyTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> propertyTypeLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String propertyTypeLibFolder = PKG_PREFIX + "/fragile_property_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path propertyTypeLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib"))
+ .compile();
+ propertyTypeLibJarMap.put(targetVersion, propertyTypeLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInProperty_getterOnly() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(propertyTypeLibJarMap.get(targetVersion))
+ // Keep property getters
+ .addKeepRules("-keep class **.Person { <init>(...); }")
+ .addKeepRules("-keepclassmembers class **.Person { *** get*(); }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectGetterOnly)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_getter", "getter_user"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".fragile_property_only_getter.Getter_userKt")
+ .assertSuccessWithOutputLines("true", "false", "Hey");
+ }
+
+ private void inspectGetterOnly(CodeInspector inspector) {
+ String personClassName = PKG + ".fragile_property_lib.Person";
+ ClassSubject person = inspector.clazz(personClassName);
+ assertThat(person, isPresent());
+ assertThat(person, not(isRenamed()));
+
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = person.getKmClass();
+ assertThat(kmClass, isPresent());
+
+ KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name");
+ assertThat(name, isPresent());
+ assertThat(name, not(isExtensionProperty()));
+ assertNotNull(name.fieldSignature());
+ assertNotNull(name.getterSignature());
+ assertNull(name.setterSignature());
+
+ KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
+ assertThat(familyName, isPresent());
+ assertThat(familyName, not(isExtensionProperty()));
+ // No backing field for property `familyName`
+ assertNull(familyName.fieldSignature());
+ assertNotNull(familyName.getterSignature());
+ // No setter for property `familyName`
+ assertNull(familyName.setterSignature());
+
+ KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
+ assertThat(age, isPresent());
+ assertThat(age, not(isExtensionProperty()));
+ assertNotNull(age.fieldSignature());
+ assertNotNull(age.getterSignature());
+ assertNull(name.setterSignature());
+ }
+
+ @Test
+ public void testMetadataInProperty_setterOnly() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(propertyTypeLibJarMap.get(targetVersion))
+ // Keep property setters (and users)
+ .addKeepRules("-keep class **.Person { <init>(...); }")
+ .addKeepRules("-keepclassmembers class **.Person { void set*(...); }")
+ .addKeepRules("-keepclassmembers class **.Person { void aging(); }")
+ .addKeepRules("-keepclassmembers class **.Person { void change*(...); }")
+ // Keep LibKt extension methods
+ .addKeepRules("-keep class **.LibKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectSetterOnly)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_setter", "setter_user"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".fragile_property_only_setter.Setter_userKt")
+ .assertSuccessWithOutputLines();
+ }
+
+ private void inspectSetterOnly(CodeInspector inspector) {
+ String personClassName = PKG + ".fragile_property_lib.Person";
+ ClassSubject person = inspector.clazz(personClassName);
+ assertThat(person, isPresent());
+ assertThat(person, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = person.getKmClass();
+ assertThat(kmClass, isPresent());
+
+ KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name");
+ assertThat(name, isPresent());
+ assertThat(name, not(isExtensionProperty()));
+ assertNull(name.fieldSignature());
+ assertNull(name.getterSignature());
+ assertNotNull(name.setterSignature());
+
+ KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
+ assertThat(familyName, not(isPresent()));
+
+ KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
+ assertThat(age, isPresent());
+ assertThat(age, not(isExtensionProperty()));
+ assertNotNull(age.fieldSignature());
+ assertNull(age.getterSignature());
+ assertNotNull(age.setterSignature());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
new file mode 100644
index 0000000..8aba4f7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2019, 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInPropertyTypeTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInPropertyTypeTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> propertyTypeLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String propertyTypeLibFolder = PKG_PREFIX + "/propertytype_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path propertyTypeLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib"))
+ .compile();
+ propertyTypeLibJarMap.put(targetVersion, propertyTypeLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInProperty_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(propertyTypeLibJarMap.get(targetVersion))
+ // Keep non-private members of Impl
+ .addKeepRules("-keep public class **.Impl { !private *; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspect)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/propertytype_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".propertytype_app.MainKt")
+ .assertSuccessWithOutputLines("Impl::8");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ String itfClassName = PKG + ".propertytype_lib.Itf";
+ String implClassName = PKG + ".propertytype_lib.Impl";
+
+ ClassSubject itf = inspector.clazz(itfClassName);
+ assertThat(itf, isRenamed());
+
+ ClassSubject impl = inspector.clazz(implClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Itf")));
+ assertTrue(superTypes.stream().anyMatch(
+ supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+ List<ClassSubject> propertyReturnTypes = kmClass.getReturnTypesInProperties();
+ assertTrue(propertyReturnTypes.stream().anyMatch(
+ propertyType -> propertyType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
new file mode 100644
index 0000000..1e33e7c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2019, 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInReturnTypeTest extends KotlinMetadataTestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInReturnTypeTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> returnTypeLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String returnTypeLibFolder = PKG_PREFIX + "/returntype_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path returnTypeLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(returnTypeLibFolder, "lib"))
+ .compile();
+ returnTypeLibJarMap.put(targetVersion, returnTypeLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInReturnType_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(returnTypeLibJarMap.get(targetVersion))
+ // Keep non-private members of Impl
+ .addKeepRules("-keep public class **.Impl { !private *; }")
+ // Keep Itf, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.Itf")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspect)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/returntype_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".returntype_app.MainKt")
+ .assertSuccessWithOutputLines("Impl::foo", "Program::foo", "true");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ String itfClassName = PKG + ".returntype_lib.Itf";
+ String implClassName = PKG + ".returntype_lib.Impl";
+
+ ClassSubject itf = inspector.clazz(itfClassName);
+ assertThat(itf, isRenamed());
+
+ ClassSubject impl = inspector.clazz(implClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Itf")));
+ assertTrue(superTypes.stream().anyMatch(
+ supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+ List<ClassSubject> functionReturnTypes = kmClass.getReturnTypesInFunctions();
+ assertTrue(functionReturnTypes.stream().anyMatch(
+ returnType -> returnType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+ }
+}
+
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
new file mode 100644
index 0000000..6cd1034
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -0,0 +1,188 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInSealedClassTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInSealedClassTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> sealedLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String sealedLibFolder = PKG_PREFIX + "/sealed_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path sealedLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(sealedLibFolder, "lib"))
+ .compile();
+ sealedLibJarMap.put(targetVersion, sealedLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInSealedClass_valid() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(sealedLibJarMap.get(targetVersion))
+ // Keep the Expr class
+ .addKeepRules("-keep class **.Expr")
+ // Keep the extension function
+ .addKeepRules("-keep class **.LibKt { <methods>; }")
+ // Keep the factory object and utils
+ .addKeepRules("-keep class **.ExprFactory { *; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectValid)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/sealed_app", "valid"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".sealed_app.ValidKt")
+ .assertSuccessWithOutputLines("6");
+ }
+
+ private void inspectValid(CodeInspector inspector) {
+ String numClassName = PKG + ".sealed_lib.Num";
+ String exprClassName = PKG + ".sealed_lib.Expr";
+ String libClassName = PKG + ".sealed_lib.LibKt";
+
+ ClassSubject num = inspector.clazz(numClassName);
+ assertThat(num, isRenamed());
+
+ ClassSubject expr = inspector.clazz(exprClassName);
+ assertThat(expr, isPresent());
+ assertThat(expr, not(isRenamed()));
+
+ KmClassSubject kmClass = expr.getKmClass();
+ assertThat(kmClass, isPresent());
+
+ kmClass.getSealedSubclassDescriptors().forEach(sealedSubclassDescriptor -> {
+ ClassSubject sealedSubclass =
+ inspector.clazz(descriptorToJavaType(sealedSubclassDescriptor));
+ assertThat(sealedSubclass, isRenamed());
+ assertEquals(sealedSubclassDescriptor, sealedSubclass.getFinalDescriptor());
+ });
+
+ ClassSubject libKt = inspector.clazz(libClassName);
+ assertThat(expr, isPresent());
+ assertThat(expr, not(isRenamed()));
+
+ KmPackageSubject kmPackage = libKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmFunctionSubject eval = kmPackage.kmFunctionExtensionWithUniqueName("eval");
+ assertThat(eval, isPresent());
+ assertThat(eval, isExtensionFunction());
+ }
+
+ @Test
+ public void testMetadataInSealedClass_invalid() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(sealedLibJarMap.get(targetVersion))
+ // Keep the Expr class
+ .addKeepRules("-keep class **.Expr")
+ // Keep the extension function
+ .addKeepRules("-keep class **.LibKt { <methods>; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectInvalid)
+ .writeToZip();
+
+ ProcessResult kotlinTestCompileResult =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/sealed_app", "invalid"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compileRaw();
+
+ assertNotEquals(0, kotlinTestCompileResult.exitCode);
+ assertThat(kotlinTestCompileResult.stderr, containsString("cannot access"));
+ assertThat(kotlinTestCompileResult.stderr, containsString("private in 'Expr'"));
+ }
+
+ private void inspectInvalid(CodeInspector inspector) {
+ String exprClassName = PKG + ".sealed_lib.Expr";
+ String numClassName = PKG + ".sealed_lib.Num";
+ String libClassName = PKG + ".sealed_lib.LibKt";
+
+ // Without any specific keep rule and no instantiation point, it's not necessary to keep
+ // sub classes of Expr.
+ ClassSubject num = inspector.clazz(numClassName);
+ assertThat(num, not(isPresent()));
+
+ ClassSubject expr = inspector.clazz(exprClassName);
+ assertThat(expr, isPresent());
+ assertThat(expr, not(isRenamed()));
+
+ KmClassSubject kmClass = expr.getKmClass();
+ assertThat(kmClass, isPresent());
+
+ assertTrue(kmClass.getSealedSubclassDescriptors().isEmpty());
+
+ ClassSubject libKt = inspector.clazz(libClassName);
+ assertThat(expr, isPresent());
+ assertThat(expr, not(isRenamed()));
+
+ KmPackageSubject kmPackage = libKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+
+ KmFunctionSubject eval = kmPackage.kmFunctionExtensionWithUniqueName("eval");
+ assertThat(eval, isPresent());
+ assertThat(eval, isExtensionFunction());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
new file mode 100644
index 0000000..ba59f4b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2019, 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInSuperTypeTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInSuperTypeTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> superTypeLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String superTypeLibFolder = PKG_PREFIX + "/supertype_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path superTypeLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(
+ getKotlinFileInTest(superTypeLibFolder, "impl"),
+ getKotlinFileInTest(superTypeLibFolder + "/internal", "itf"))
+ .compile();
+ superTypeLibJarMap.put(targetVersion, superTypeLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInSupertype_merged() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(superTypeLibJarMap.get(targetVersion))
+ // Keep non-private members except for ones in `internal` definitions.
+ .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectMerged)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
+ .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
+ }
+
+ private void inspectMerged(CodeInspector inspector) {
+ String itfClassName = PKG + ".supertype_lib.internal.Itf";
+ String implClassName = PKG + ".supertype_lib.Impl";
+
+ assertThat(inspector.clazz(itfClassName), not(isPresent()));
+
+ ClassSubject impl = inspector.clazz(implClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("internal")));
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Itf")));
+ }
+
+ @Test
+ public void testMetadataInSupertype_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(superTypeLibJarMap.get(targetVersion))
+ // Keep non-private members except for ones in `internal` definitions.
+ .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
+ // Keep `internal` definitions, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.internal.** { *; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspectRenamed)
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
+ .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
+ }
+
+ private void inspectRenamed(CodeInspector inspector) {
+ String itfClassName = PKG + ".supertype_lib.internal.Itf";
+ String implClassName = PKG + ".supertype_lib.Impl";
+
+ ClassSubject itf = inspector.clazz(itfClassName);
+ assertThat(itf, isRenamed());
+
+ ClassSubject impl = inspector.clazz(implClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ KmClassSubject kmClass = impl.getKmClass();
+ assertThat(kmClass, isPresent());
+ List<ClassSubject> superTypes = kmClass.getSuperTypes();
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("internal")));
+ assertTrue(superTypes.stream().noneMatch(
+ supertype -> supertype.getFinalDescriptor().contains("Itf")));
+ assertTrue(superTypes.stream().anyMatch(
+ supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
new file mode 100644
index 0000000..8b9f276
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInTypeAliasTest extends KotlinMetadataTestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRewriteInTypeAliasTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static final Map<KotlinTargetVersion, Path> typeAliasLibJarMap = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String typeAliasLibFolder = PKG_PREFIX + "/typealias_lib";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path typeAliasLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(typeAliasLibFolder, "lib"))
+ .compile();
+ typeAliasLibJarMap.put(targetVersion, typeAliasLibJar);
+ }
+ }
+
+ @Test
+ public void testMetadataInTypeAlias_renamed() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(typeAliasLibJarMap.get(targetVersion))
+ // Keep non-private members of Impl
+ .addKeepRules("-keep class **.Impl { !private *; }")
+ // Keep Itf, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.Itf")
+ // Keep LibKt that contains the type-aliases and utils.
+ .addKeepRules("-keep class **.LibKt { *; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspect)
+ .writeToZip();
+
+ ProcessResult kotlinTestCompileResult =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typealias_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ // TODO(b/70169921): update to just .compile() once fixed.
+ .compileRaw();
+ // TODO(b/70169921): should be able to compile!
+ assertNotEquals(0, kotlinTestCompileResult.exitCode);
+ assertThat(
+ kotlinTestCompileResult.stderr,
+ containsString(
+ "type mismatch: inferred type is ProgramClass but API /* = Itf */ was expected"));
+ }
+
+ private void inspect(CodeInspector inspector) {
+ String itfClassName = PKG + ".typealias_lib.Itf";
+ String libKtClassName = PKG + ".typealias_lib.LibKt";
+
+ ClassSubject itf = inspector.clazz(itfClassName);
+ assertThat(itf, isRenamed());
+
+ ClassSubject libKt = inspector.clazz(libKtClassName);
+ assertThat(libKt, isPresent());
+ assertThat(libKt, not(isRenamed()));
+
+ MethodSubject seq = libKt.uniqueMethodWithName("seq");
+ assertThat(seq, isPresent());
+ assertThat(seq, not(isRenamed()));
+
+ // API entry is kept, hence the presence of Metadata.
+ KmPackageSubject kmPackage = libKt.getKmPackage();
+ assertThat(kmPackage, isPresent());
+ // TODO(b/70169921): need further inspection: many kinds of type appearances in typealias.
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 35dd597..f93179f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -51,9 +52,11 @@
.addKeepMainRule(mainClassName)
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.addKeepRules("-keep class kotlin.Metadata")
- // TODO(b/145090972): Should never need to exit gracefully during testing.
- .allowClassInlinerGracefulExit()
+ .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), mainClassName);
CodeInspector inspector = result.inspector();
ClassSubject clazz = inspector.clazz(mainClassName);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt
new file mode 100644
index 0000000..f0ddbf7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt
@@ -0,0 +1,25 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata.typealias_app
+
+import com.android.tools.r8.kotlin.metadata.typealias_lib.API
+import com.android.tools.r8.kotlin.metadata.typealias_lib.Impl
+import com.android.tools.r8.kotlin.metadata.typealias_lib.seq
+
+class ProgramClass : Impl() {
+ override fun foo(): API {
+ super.foo()
+ println("Program::foo")
+ return this
+ }
+}
+
+fun main() {
+ val instance = ProgramClass()
+ val l = seq(instance)
+ for (api in l) {
+ println(api == api.foo())
+ println(api.hey())
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib.kt
new file mode 100644
index 0000000..82ba761
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib.kt
@@ -0,0 +1,64 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata.typealias_lib
+
+// TypeAlias {
+// name: "Jude"
+// underlyingType {
+// classifier: "kotlin/Long"
+// }
+// }
+typealias Jude = Long
+
+interface Itf {
+ fun foo() : Itf
+ fun hey() : Jude
+}
+
+// TypeAlias {
+// name: "API"
+// underlyingType {
+// classifier: ".../Itf"
+// }
+// }
+typealias API = Itf
+// TypeAlias {
+// typeParameters { KmTypeParameter { name = "T" ... } }
+// name: "myAliasedList"
+// underlyingType {
+// classifier: "kotlin/Array"
+// }
+// expandedType == underlyingType
+// }
+typealias myAliasedArray<T> = Array<T>
+// TypeAlias {
+// underlyingType {
+// classifier: ".../myAliasedArray"
+// arguments {
+// KmTypeProjection { ... type = ".../API" }
+// }
+// }
+// expandedType {
+// classifier: "kotlin/Array"
+// arguments {
+// KmTypeProjection { ... type = ".../Itf" }
+// }
+// }
+// }
+typealias APIs = myAliasedArray<API>
+
+open class Impl : API {
+ override fun foo() : API {
+ println("Impl::foo")
+ return this
+ }
+
+ override fun hey(): Jude {
+ return 42L
+ }
+}
+
+fun seq(vararg itfs : Itf) : APIs {
+ return arrayOf(*itfs)
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 3806c05..8bc0e79 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.withNativeFileSeparators;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import com.android.tools.r8.GenerateMainDexList;
import com.android.tools.r8.GenerateMainDexListCommand;
@@ -328,12 +329,14 @@
.addKeepRules("-keepattributes *Annotation*")
.addMainDexRuleFiles(mainDexRules)
.addOptionsModification(optionsConsumer)
+ .allowDiagnosticWarningMessages()
.assumeAllMethodsMayHaveSideEffects()
.setMinApi(minSdk)
.noMinification()
.noTreeShaking()
.setMainDexListConsumer(ToolHelper.consumeString(r8MainDexListOutput::set))
.compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.writeToZip(out);
List<String> r8MainDexList =
diff --git a/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
index 3778392..bf33b1c 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
@@ -48,6 +48,7 @@
.addKeepMainRule(mainClass)
// Include explicit main dex entry for class Static.
.addMainDexListClasses(Static.class)
+ .allowDiagnosticWarningMessages()
.compile()
.inspect(this::classStaticGone)
.assertOnlyWarnings()
@@ -66,6 +67,7 @@
.addMainDexListClasses(Main.class, Static.class)
// Include main dex rule for class Static2.
.addMainDexClassRules(Static2.class)
+ .allowDiagnosticWarningMessages()
.compile()
.inspect(this::classStaticGone)
.assertOnlyWarnings()
diff --git a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
index d56ae8e..7e15524 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -33,7 +34,7 @@
@Parameterized.Parameters(name = "{0} target: {1} minify: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimes().build(),
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
KotlinTargetVersion.values(),
BooleanUtils.values());
}
@@ -52,8 +53,12 @@
.addProgramFiles(getKotlinJarFile(FOLDER))
.addProgramFiles(getJavaJarFile(FOLDER))
.addKeepMainRule(MAIN_CLASS_NAME)
+ .allowDiagnosticWarningMessages()
.minification(minify)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), MAIN_CLASS_NAME)
.inspector();
ClassSubject enumClass = inspector.clazz(ENUM_CLASS_NAME);
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index e9383ef..1c7c5bc 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
import static com.android.tools.r8.utils.DescriptorUtils.isValidJavaType;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -46,6 +47,7 @@
private final String appFileName;
private final List<String> keepRulesFiles;
private final BiConsumer<TestParameters, CodeInspector> inspection;
+ private final String test;
public IdentifierMinifierTest(
TestParameters parameters,
@@ -56,18 +58,23 @@
this.appFileName = ToolHelper.EXAMPLES_BUILD_DIR + test + FileUtils.JAR_EXTENSION;
this.keepRulesFiles = keepRulesFiles;
this.inspection = inspection;
+ this.test = test;
}
@Test
public void identiferMinifierTest() throws Exception {
+ boolean hasWarning =
+ test.equals("identifiernamestring") && keepRulesFiles.get(0).endsWith("keep-rules-2.txt");
CodeInspector codeInspector =
testForR8(parameters.getBackend())
.addProgramFiles(Paths.get(appFileName))
.addKeepRuleFiles(ListUtils.map(keepRulesFiles, Paths::get))
- .allowUnusedProguardConfigurationRules()
+ .allowDiagnosticWarningMessages(hasWarning)
.enableProguardTestOptions()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllWarningMessagesMatch(
+ containsString("Cannot determine what identifier string flows to"))
.inspector();
inspection.accept(parameters, codeInspector);
}
@@ -95,7 +102,7 @@
Collection<Object[]> parameters = NamingTestBase.createTests(tests, inspections);
List<Object[]> parametersWithBackend = new ArrayList<>();
- for (TestParameters testParameter : getTestParameters().withAllRuntimes().build()) {
+ for (TestParameters testParameter : getTestParameters().withAllRuntimesAndApiLevels().build()) {
for (Object[] row : parameters) {
Object[] newRow = new Object[row.length + 1];
newRow[0] = testParameter;
@@ -315,5 +322,4 @@
});
return result;
}
-
}
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
index 2b06482..e9ce1d8 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
@@ -3,11 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.code.AputObject;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.ConstClass;
@@ -53,7 +57,7 @@
"-keep class " + CLASS_NAME,
"-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { !static <fields>; }",
"-dontoptimize");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -86,7 +90,7 @@
"-keep class " + CLASS_NAME,
"-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { !static <fields>; }",
"-dontoptimize");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -129,7 +133,7 @@
"-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { !static <fields>; }",
"-keep,allowobfuscation class " + BOO,
"-dontoptimize");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -167,7 +171,7 @@
"-keep class " + CLASS_NAME,
"-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { static <fields>; }",
"-dontoptimize");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -199,7 +203,7 @@
"-keep class " + CLASS_NAME,
"-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { static <fields>; }",
"-dontoptimize");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -238,7 +242,7 @@
"-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { static <fields>; }",
"-keep,allowobfuscation class " + BOO,
"-dontoptimize");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -268,7 +272,7 @@
"-identifiernamestring class " + CLASS_NAME + " { static java.lang.String sClassName; }",
"-keep class " + CLASS_NAME + " { static java.lang.String sClassName; }",
"-dontshrink");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -290,7 +294,7 @@
"-keep class " + CLASS_NAME + " { static java.lang.String sClassName; }",
"-keep,allowobfuscation class " + BOO,
"-dontshrink");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -314,7 +318,7 @@
"-keep class " + CLASS_NAME + " { static java.lang.String sFieldName; }",
"-keep,allowobfuscation class " + BOO + " { <fields>; }",
"-dontshrink");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -338,7 +342,7 @@
"-keep class " + CLASS_NAME + " { static java.lang.String sMethodName; }",
"-keep,allowobfuscation class " + BOO + " { <methods>; }",
"-dontshrink");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -365,10 +369,20 @@
"invoke-static {v0, v1}, LExample;->foo(Ljava/lang/String;Ljava/lang/String;)V",
"return-void");
- List<String> pgConfigs = ImmutableList.of(
- "-identifiernamestring class " + CLASS_NAME + " { static void foo(...); }",
- "-keep class " + CLASS_NAME);
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector =
+ compileWithR8(
+ builder,
+ testBuilder ->
+ testBuilder
+ .addKeepRules(
+ "-identifiernamestring class "
+ + CLASS_NAME
+ + " { static void foo(...); }",
+ "-keep class " + CLASS_NAME)
+ .allowDiagnosticWarningMessages())
+ .assertAllWarningMessagesMatch(
+ containsString("Cannot determine what 'Mixed/form.Boo' refers to"))
+ .inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -410,7 +424,7 @@
List<String> pgConfigs = ImmutableList.of(
"-identifiernamestring class " + CLASS_NAME + " { static void foo(...); }",
"-keep class " + CLASS_NAME);
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -456,7 +470,7 @@
"-identifiernamestring class " + CLASS_NAME + " { static void foo(...); }",
"-keep class " + CLASS_NAME,
"-keep,allowobfuscation class " + BOO);
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -510,7 +524,7 @@
+ "}",
"-keep class " + CLASS_NAME,
"-keep class R { *; }");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -560,7 +574,7 @@
+ "}",
"-keep class " + CLASS_NAME,
"-keep,allowobfuscation class R { *; }");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -617,7 +631,7 @@
+ "}",
"-keep class " + CLASS_NAME,
"-keep class R { *; }");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -678,7 +692,7 @@
+ "}",
"-keep class " + CLASS_NAME,
"-keep,allowobfuscation class R { *; }");
- CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+ CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
ClassSubject clazz = inspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
@@ -702,13 +716,17 @@
assertNotEquals("foo", constString.getString().toString());
}
- private CodeInspector getInspectorAfterRunR8(
+ private R8TestCompileResult compileWithR8(
SmaliBuilder builder, List<String> proguardConfigurations) throws Exception {
+ return compileWithR8(builder, testBuilder -> testBuilder.addKeepRules(proguardConfigurations));
+ }
+
+ private R8TestCompileResult compileWithR8(
+ SmaliBuilder builder, ThrowableConsumer<R8FullTestBuilder> configuration) throws Exception {
return testForR8(Backend.DEX)
.addProgramDexFileData(builder.compile())
- .addKeepRules(proguardConfigurations)
+ .apply(configuration)
.debug()
- .compile()
- .inspector();
+ .compile();
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java b/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java
new file mode 100644
index 0000000..9a55cee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IntersectionLambdaTest extends TestBase {
+
+ private static final String[] EXPECTED = new String[] {"Lambda.foo", "J.bar", "K.baz"};
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public IntersectionLambdaTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addInnerClasses(IntersectionLambdaTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(IntersectionLambdaTest.class)
+ .enableMergeAnnotations()
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @FunctionalInterface
+ @NeverMerge
+ public interface I {
+ void foo();
+ }
+
+ public interface J {
+ default void bar() {
+ System.out.println("J.bar");
+ }
+ }
+
+ public interface K {
+ default void baz() {
+ System.out.println("K.baz");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ callI((I & J & K) () -> System.out.println("Lambda.foo"));
+ }
+
+ private static void callI(I i) {
+ i.foo();
+ ((J) i).bar();
+ ((K) i).baz();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/IntersectionWithInheritanceLambdaTest.java b/src/test/java/com/android/tools/r8/naming/IntersectionWithInheritanceLambdaTest.java
new file mode 100644
index 0000000..f784a8f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/IntersectionWithInheritanceLambdaTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IntersectionWithInheritanceLambdaTest extends TestBase {
+
+ private static final String[] EXPECTED = new String[] {"Lambda.foo", "J.bar", "K.baz"};
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public IntersectionWithInheritanceLambdaTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addInnerClasses(IntersectionWithInheritanceLambdaTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(IntersectionWithInheritanceLambdaTest.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ public interface J {
+ default void bar() {
+ System.out.println("J.bar");
+ }
+ }
+
+ public interface K extends J {
+ void foo();
+
+ default void baz() {
+ System.out.println("K.baz");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ callFooBarBaz(() -> System.out.println("Lambda.foo"));
+ }
+
+ private static void callFooBarBaz(K k) {
+ k.foo();
+ k.bar();
+ k.baz();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/InvalidObfuscationEntryTest.java b/src/test/java/com/android/tools/r8/naming/InvalidObfuscationEntryTest.java
index 982267f..57bd145 100644
--- a/src/test/java/com/android/tools/r8/naming/InvalidObfuscationEntryTest.java
+++ b/src/test/java/com/android/tools/r8/naming/InvalidObfuscationEntryTest.java
@@ -48,7 +48,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public InvalidObfuscationEntryTest(TestParameters parameters) {
@@ -65,13 +65,10 @@
.addKeepRules("-obfuscationdictionary " + dictionary.toString())
.addKeepAllClassesRuleWithAllowObfuscation()
.addKeepMainRule(Main.class)
- .setMinApi(parameters.getRuntime())
+ .allowDiagnosticInfoMessages()
+ .setMinApi(parameters.getApiLevel())
.compile()
- .inspectDiagnosticMessages(
- testDiagnosticMessages -> {
- testDiagnosticMessages.assertInfoMessageThatMatches(
- containsString("Invalid character"));
- })
+ .assertInfoMessageThatMatches(containsString("Invalid character"))
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Hello from a", "Hello from b")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
index 7828143..daa21a4 100644
--- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -67,12 +68,16 @@
public void test_example3() throws Exception {
TestKotlinClass ex3 = new TestKotlinClass("intrinsics_identifiers.Example3Kt");
String mainClassName = ex3.getClassName();
- TestCompileResult result = testForR8(Backend.DEX)
- .addProgramFiles(getKotlinJarFile(FOLDER))
- .addProgramFiles(getJavaJarFile(FOLDER))
- .addKeepMainRule(mainClassName)
- .minification(minification)
- .compile();
+ TestCompileResult result =
+ testForR8(Backend.DEX)
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .addKeepMainRule(mainClassName)
+ .allowDiagnosticWarningMessages()
+ .minification(minification)
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."));
CodeInspector codeInspector = result.inspector();
MethodSubject main = codeInspector.clazz(ex3.getClassName()).mainMethod();
assertThat(main, isPresent());
@@ -116,18 +121,23 @@
String targetFieldName,
String targetMethodName) throws Exception {
String mainClassName = testMain.getClassName();
- TestRunResult result = testForR8(Backend.DEX)
- .addProgramFiles(getKotlinJarFile(FOLDER))
- .addProgramFiles(getJavaJarFile(FOLDER))
- .enableProguardTestOptions()
- .addKeepMainRule(mainClassName)
- .addKeepRules(StringUtils.lines(
- "-neverclassinline class **." + targetClassName,
- "-nevermerge class **." + targetClassName,
- "-neverinline class **." + targetClassName + " { <methods>; }"
- ))
- .minification(minification)
- .run(mainClassName);
+ TestRunResult result =
+ testForR8(Backend.DEX)
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .enableProguardTestOptions()
+ .addKeepMainRule(mainClassName)
+ .addKeepRules(
+ StringUtils.lines(
+ "-neverclassinline class **." + targetClassName,
+ "-nevermerge class **." + targetClassName,
+ "-neverinline class **." + targetClassName + " { <methods>; }"))
+ .allowDiagnosticWarningMessages()
+ .minification(minification)
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(mainClassName);
CodeInspector codeInspector = result.inspector();
MethodSubject main = codeInspector.clazz(testMain.getClassName()).mainMethod();
diff --git a/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java b/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java
index 44e95ec..a391a21 100644
--- a/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java
@@ -4,13 +4,7 @@
package com.android.tools.r8.proguard.configuration;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.TestBase;
-import java.util.List;
import org.junit.Test;
public class UnusedKeepRuleTest extends TestBase {
@@ -19,20 +13,8 @@
public void test() throws Exception {
testForR8(Backend.DEX)
.addKeepRules("-keep class NotPresent")
- .addOptionsModification(
- options -> options.testing.reportUnusedProguardConfigurationRules = true)
.allowUnusedProguardConfigurationRules()
.compile()
- .inspectDiagnosticMessages(
- messages -> {
- messages.assertOnlyInfos();
- List<Diagnostic> infos = messages.getInfos();
- assertEquals(1, infos.size());
-
- Diagnostic info = infos.get(0);
- assertThat(
- info.getDiagnosticMessage(),
- containsString("Proguard configuration rule does not match anything"));
- });
+ .assertInfosCount(1);
}
}
diff --git a/src/test/java/com/android/tools/r8/proguard/rules/InnerClassNameSeparatorTest.java b/src/test/java/com/android/tools/r8/proguard/rules/InnerClassNameSeparatorTest.java
index 6da44a5..bb37f6f 100644
--- a/src/test/java/com/android/tools/r8/proguard/rules/InnerClassNameSeparatorTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/rules/InnerClassNameSeparatorTest.java
@@ -7,6 +7,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
@@ -27,7 +28,7 @@
@Parameterized.Parameters(name = "{0}, separator: {1}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimes().build(), ImmutableList.of("$", "."));
+ getTestParameters().withAllRuntimesAndApiLevels().build(), ImmutableList.of("$", "."));
}
public InnerClassNameSeparatorTest(TestParameters parameters, String separator) {
@@ -37,17 +38,23 @@
@Test
public void testR8() throws Exception {
- TestRunResult<?> result =
- runTest(
- testForR8(parameters.getBackend())
- .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
- .addOptionsModification(
- options -> {
- if (separator.equals(".")) {
- // R8 currently does not recognize the '.' as an inner class name separator.
- options.testing.allowUnusedProguardConfigurationRules = true;
- }
- }));
+ R8TestRunResult result =
+ testForR8(parameters.getBackend())
+ .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
+ .allowUnusedProguardConfigurationRules(separator.equals("."))
+ .addProgramClassesAndInnerClasses(InnerClassNameSeparatorTestClass.class)
+ .addKeepMainRule(InnerClassNameSeparatorTestClass.class)
+ .addKeepRules(
+ "-keep,allowobfuscation class "
+ + InnerClassNameSeparatorTestClass.class.getTypeName()
+ + separator
+ + InnerClassNameSeparatorTestClass.Inner.class.getSimpleName()
+ + " {",
+ " <init>(...);",
+ "}")
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), InnerClassNameSeparatorTestClass.class);
if (separator.equals("$")) {
result.assertSuccessWithOutputLines("Hello world!");
} else {
@@ -73,7 +80,7 @@
+ " {",
" <init>(...);",
"}")
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), InnerClassNameSeparatorTestClass.class);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
index db1864c..2bd5e75 100644
--- a/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
+++ b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.resolution;
+import static org.hamcrest.CoreMatchers.containsString;
+
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -47,9 +49,11 @@
.enableInliningAnnotations()
.addKeepClassRules(ProgramClass.class)
.addKeepMainRule(ProgramTestRunnerWithoutPhi.class)
+ .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.debug()
.compile()
+ .assertAllWarningMessagesMatch(containsString("extends program class"))
.addRunClasspathClasses(LibraryClass.class)
.run(parameters.getRuntime(), ProgramTestRunnerWithoutPhi.class)
.assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
@@ -64,8 +68,10 @@
.enableInliningAnnotations()
.addKeepClassRules(ProgramClass.class)
.addKeepMainRule(ProgramTestRunnerWithPhi.class)
+ .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllWarningMessagesMatch(containsString("extends program class"))
.addRunClasspathClasses(LibraryClass.class)
.run(parameters.getRuntime(), ProgramTestRunnerWithPhi.class)
.assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
index d2a1ee6..609b506 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.resolution.interfacetargets;
-import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -49,7 +49,8 @@
public void testResolution() throws Exception {
assumeTrue(parameters.useRuntimeAsNoneRuntime());
AppInfoWithLiveness appInfo =
- computeAppViewWithLiveness(buildClasses(I.class, A.class, Main.class).build(), Main.class)
+ computeAppViewWithLiveness(
+ buildClasses(I.class, J.class, A.class, Main.class).build(), Main.class)
.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
@@ -59,8 +60,7 @@
.collect(Collectors.toSet());
ImmutableSet<String> expected =
ImmutableSet.of(A.class.getTypeName() + ".bar", J.class.getTypeName() + ".bar");
- // TODO(b/148168065): Correct incorrect target lookup.
- // assertEquals(expected, targets);
+ assertEquals(expected, targets);
}
@Test
@@ -81,9 +81,7 @@
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatMatches(containsString("AbstractMethodError"));
- // TODO(b/148168065): Correct incorrect target lookup.
- // .assertSuccessWithOutputLines(EXPECTED);
+ .assertSuccessWithOutputLines(EXPECTED);
}
@FunctionalInterface
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
new file mode 100644
index 0000000..65448a0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
@@ -0,0 +1,155 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.interfacetargets;
+
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.transformers.ClassTransformer;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Matcher;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.MethodVisitor;
+
+@RunWith(Parameterized.class)
+public class InvokeInterfaceClInitTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ static {
+ }
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InvokeInterfaceClInitTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.useRuntimeAsNoneRuntime());
+ AppInfoWithLiveness appInfo =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, B.class)
+ .addClassProgramData(transformI(), transformMain())
+ .build(),
+ Main.class)
+ .appInfo();
+ DexMethod method = buildNullaryVoidMethod(I.class, "<clinit>", appInfo.dexItemFactory());
+ Assert.assertThrows(
+ AssertionError.class,
+ () -> {
+ appInfo.resolveMethod(method.holder, method).lookupInterfaceTargets(appInfo);
+ });
+ }
+
+ private Matcher<String> getExpected() {
+ if (parameters.getRuntime().isCf()) {
+ Matcher<String> expected = containsString("java.lang.VerifyError");
+ // JDK 9 and 11 output VerifyError or ClassFormatError non-deterministically.
+ if (parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK9)) {
+ expected = CoreMatchers.anyOf(expected, containsString("java.lang.ClassFormatError"));
+ }
+ return expected;
+ }
+ assert parameters.getRuntime().isDex();
+ DexRuntime dexRuntime = parameters.getRuntime().asDex();
+ if (dexRuntime.getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
+ return containsString("NoSuchMethodError");
+ }
+ if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
+ return containsString("java.lang.ClassNotFoundException");
+ }
+ return containsString("java.lang.VerifyError");
+ }
+
+ @Test
+ public void testRuntimeClInit()
+ throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addProgramClasses(A.class, B.class)
+ .addProgramClassFileData(transformMain(), transformI())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(getExpected());
+ }
+
+ @Test
+ public void testR8ClInit() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, B.class)
+ .addProgramClassFileData(transformMain(), transformI())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(getExpected());
+ }
+
+ private byte[] transformI() throws IOException {
+ return transformer(I.class)
+ .addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public MethodVisitor visitMethod(
+ int access,
+ String name,
+ String descriptor,
+ String signature,
+ String[] exceptions) {
+ return super.visitMethod(access, "<clinit>", descriptor, signature, exceptions);
+ }
+ })
+ .transform();
+ }
+
+ private byte[] transformMain() throws IOException {
+ return transformer(Main.class)
+ .transformMethodInsnInMethod(
+ "callClInit",
+ (opcode, owner, name, descriptor, isInterface, continuation) ->
+ continuation.apply(opcode, owner, "<clinit>", descriptor, isInterface))
+ .transform();
+ }
+
+ public interface I {
+
+ default void foo() { // <-- will be rewritten to <clinit>
+ System.out.println("I.foo");
+ }
+ }
+
+ public static class A implements I {}
+
+ public static class B implements I {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ callClInit(args.length == 0 ? new A() : new B());
+ }
+
+ private static void callClInit(I i) {
+ i.foo(); // <-- will be i.<clinit>()
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
new file mode 100644
index 0000000..ede5785
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -0,0 +1,131 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.interfacetargets;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InvokeInterfaceWithStaticTargetTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InvokeInterfaceWithStaticTargetTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.getRuntime().equals(TestRuntime.getDefaultJavaRuntime()));
+ AppInfoWithLiveness appInfo =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, I.class).addClassProgramData(transformMain()).build(),
+ Main.class)
+ .appInfo();
+ DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory());
+ Assert.assertThrows(
+ AssertionError.class,
+ () -> appInfo.resolveMethod(method.holder, method).lookupInterfaceTargets(appInfo));
+ }
+
+ @Test
+ public void testRuntimeClInit()
+ throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addProgramClasses(A.class, I.class)
+ .addProgramClassFileData(transformMain())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(containsString(getExpected()));
+ }
+
+ @Test
+ public void testR8ClInit() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, I.class)
+ .addProgramClassFileData(transformMain())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(containsString(getExpected()));
+ }
+
+ private String getExpected() {
+ return parameters.isCfRuntime()
+ || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
+ ? "IncompatibleClassChangeError"
+ : "NoSuchMethodError";
+ }
+
+ private byte[] transformMain() throws IOException {
+ return transformer(Main.class)
+ .transformMethodInsnInMethod(
+ "callFooBar",
+ (opcode, owner, name, descriptor, isInterface, continuation) -> {
+ if (name.equals("notify")) {
+ continuation.apply(
+ INVOKEINTERFACE,
+ DescriptorUtils.getBinaryNameFromJavaType(I.class.getTypeName()),
+ "bar",
+ descriptor,
+ true);
+ } else {
+ continuation.apply(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform();
+ }
+
+ public interface I {
+
+ void foo();
+
+ static void bar() {
+ System.out.println("I.bar");
+ }
+ }
+
+ public static class A implements I {
+
+ @Override
+ public void foo() {
+ System.out.println("A.foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ callFooBar(args.length == 0 ? () -> System.out.println("Lambda.foo") : new A());
+ }
+
+ public static void callFooBar(I i) {
+ i.foo();
+ i.notify(); // <-- will be i.bar()
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index 5d2a2d8..13190d9 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -10,6 +10,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
@@ -86,8 +87,11 @@
.addKeepAttributes("SourceFile", "LineNumberTable")
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(MAIN)
+ .allowDiagnosticWarningMessages()
.noMinification()
.setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), MAIN)
.assertFailureWithErrorThatMatches(containsString("main"))
.inspectStackTrace(
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index e2fe820..2d9f47b 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -11,6 +11,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
@@ -81,9 +82,12 @@
.addProgramFiles(compilationResults.apply(parameters.getRuntime()))
.addProgramFiles(ToolHelper.getKotlinStdlibJar())
.addKeepAttributes("SourceFile", "LineNumberTable")
+ .allowDiagnosticWarningMessages()
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(main)
.setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), main)
.assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
.inspectStackTrace(
@@ -106,9 +110,12 @@
.addProgramFiles(compilationResults.apply(parameters.getRuntime()))
.addProgramFiles(ToolHelper.getKotlinStdlibJar())
.addKeepAttributes("SourceFile", "LineNumberTable")
+ .allowDiagnosticWarningMessages()
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(main)
.setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), main)
.assertFailureWithErrorThatMatches(containsString("inlineExceptionInstance"))
.inspectStackTrace(
@@ -131,9 +138,12 @@
.addProgramFiles(compilationResults.apply(parameters.getRuntime()))
.addProgramFiles(ToolHelper.getKotlinStdlibJar())
.addKeepAttributes("SourceFile", "LineNumberTable")
+ .allowDiagnosticWarningMessages()
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(main)
.setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), main)
.assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
.inspectStackTrace(
@@ -158,9 +168,12 @@
.addProgramFiles(compilationResults.apply(parameters.getRuntime()))
.addProgramFiles(ToolHelper.getKotlinStdlibJar())
.addKeepAttributes("SourceFile", "LineNumberTable")
+ .allowDiagnosticWarningMessages()
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(main)
.setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), main)
.assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
.inspectStackTrace(
diff --git a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
index 5143949..b299b76 100644
--- a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.rewrite;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -26,7 +29,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public JavaScriptScriptEngineTest(TestParameters parameters) {
@@ -41,7 +44,7 @@
assumeTrue("Only run D8 for dex backend", parameters.isDexRuntime());
testForD8()
.addInnerClasses(JavaScriptScriptEngineTest.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.apply(this::addRhinoForAndroid)
.compile()
.run(parameters.getRuntime(), TestClassWithExplicitRhinoScriptEngineRegistration.class)
@@ -53,15 +56,21 @@
testForR8(parameters.getBackend())
.addInnerClasses(JavaScriptScriptEngineTest.class)
.addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.apply(
b -> {
if (parameters.isDexRuntime()) {
addRhinoForAndroid(b);
addKeepRulesForAndroidRhino(b);
+ b.allowDiagnosticWarningMessages();
}
})
.compile()
+ .assertAllWarningMessagesMatch(
+ anyOf(
+ containsString("Missing class:"),
+ containsString("required for default or static interface methods desugaring"),
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(
parameters.isCfRuntime() ? EXPECTED_NASHORN_OUTPUT : EXPECTED_RHINO_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
index 4e451ee..c7037ca 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
@@ -4,12 +4,14 @@
package com.android.tools.r8.rewrite;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DataEntryResource;
-import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.origin.Origin;
@@ -41,7 +43,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ScriptEngineTest(TestParameters parameters) {
@@ -51,33 +53,37 @@
@Test
public void test() throws IOException, CompilationFailedException, ExecutionException {
Path path = temp.newFile("out.zip").toPath();
- R8FullTestBuilder builder =
- testForR8(parameters.getBackend())
- .addInnerClasses(ScriptEngineTest.class)
- .addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getRuntime())
- .addDataEntryResources(
- DataEntryResource.fromBytes(
- StringUtils.lines(MyScriptEngine1FactoryImpl.class.getTypeName()).getBytes(),
- "META-INF/services/" + ScriptEngineFactory.class.getTypeName(),
- Origin.unknown()))
- .addDataEntryResources(
- DataEntryResource.fromBytes(
- StringUtils.lines(MyScriptEngine2FactoryImpl.class.getTypeName()).getBytes(),
- "META-INF/services/" + ScriptEngineFactory.class.getTypeName(),
- Origin.unknown()))
- .apply(
- b -> {
- if (parameters.isDexRuntime()) {
- addRhinoForAndroid(b);
- }
- })
- // TODO(b/136633154): This should work both with and without -dontobfuscate.
- .noMinification()
- // TODO(b/136633154): This should work both with and without -dontshrink.
- .noTreeShaking();
- builder
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ScriptEngineTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .addDataEntryResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines(MyScriptEngine1FactoryImpl.class.getTypeName()).getBytes(),
+ "META-INF/services/" + ScriptEngineFactory.class.getTypeName(),
+ Origin.unknown()))
+ .addDataEntryResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines(MyScriptEngine2FactoryImpl.class.getTypeName()).getBytes(),
+ "META-INF/services/" + ScriptEngineFactory.class.getTypeName(),
+ Origin.unknown()))
+ .apply(
+ b -> {
+ if (parameters.isDexRuntime()) {
+ addRhinoForAndroid(b);
+ b.allowDiagnosticWarningMessages();
+ }
+ })
+ // TODO(b/136633154): This should work both with and without -dontobfuscate.
+ .noMinification()
+ // TODO(b/136633154): This should work both with and without -dontshrink.
+ .noTreeShaking()
.compile()
+ .assertAllWarningMessagesMatch(
+ anyOf(
+ containsString("Missing class:"),
+ containsString("it is required for default or static interface methods desugaring"),
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
.writeToZip(path)
.run(parameters.getRuntime(), TestClass.class)
// TODO(b/136633154): This should provide 2 script engines on both runtimes. The use of
diff --git a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalStaticFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalStaticFieldsTest.java
index 6f18cdc..302cb28 100644
--- a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalStaticFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalStaticFieldsTest.java
@@ -68,8 +68,7 @@
mainMethod
.streamInstructions()
.noneMatch(i -> i.isConstString("Dead code: 1", JumboStringMode.ALLOW)));
- // TODO(b/138913138): effectively final, and default value is set.
- assertFalse(
+ assertTrue(
mainMethod
.streamInstructions()
.noneMatch(i -> i.isConstString("Dead code: 2", JumboStringMode.ALLOW)));
@@ -97,8 +96,7 @@
mainMethod
.streamInstructions()
.noneMatch(i -> i.isConstString("Dead code: 7", JumboStringMode.ALLOW)));
- // TODO(b/138913138): effectively final, and default value is set.
- assertFalse(
+ assertTrue(
mainMethod
.streamInstructions()
.noneMatch(i -> i.isConstString("Dead code: 8", JumboStringMode.ALLOW)));
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index b42be41..5b119ce 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -7,6 +7,7 @@
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.core.IsNot.not;
import com.android.tools.r8.D8TestRunResult;
@@ -79,7 +80,9 @@
@Parameters(name = "{0}, mode: {1}, use interface: {2}")
public static Collection<Object[]> parameters() {
return buildParameters(
- getTestParameters().withAllRuntimes().build(), Mode.values(), BooleanUtils.values());
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ Mode.values(),
+ BooleanUtils.values());
}
@Test
@@ -171,7 +174,8 @@
jasminBuilder.writeJar(inputJar);
if (parameters.isCfRuntime()) {
- TestRunResult<?> jvmResult = testForJvm().addClasspath(inputJar).run(mainClass.name);
+ TestRunResult<?> jvmResult =
+ testForJvm().addClasspath(inputJar).run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(jvmResult, Compiler.JAVAC);
ProguardTestRunResult proguardResult =
@@ -184,10 +188,12 @@
} else {
assert parameters.isDexRuntime();
- DXTestRunResult dxResult = testForDX().addProgramFiles(inputJar).run(mainClass.name);
+ DXTestRunResult dxResult =
+ testForDX().addProgramFiles(inputJar).run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(dxResult, Compiler.DX);
- D8TestRunResult d8Result = testForD8().addProgramFiles(inputJar).run(mainClass.name);
+ D8TestRunResult d8Result =
+ testForD8().addProgramFiles(inputJar).run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(d8Result, Compiler.D8);
}
@@ -205,7 +211,14 @@
options.testing.allowTypeErrors = true;
}
})
- .setMinApi(parameters.getRuntime())
+ .allowDiagnosticWarningMessages(
+ mode == Mode.INVOKE_UNVERIFIABLE_METHOD && !useInterface)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo(
+ "The method `void UnverifiableClass.unverifiableMethod()` does not type check"
+ + " and will be assumed to be unreachable."))
.run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(r8Result, Compiler.R8);
@@ -224,7 +237,14 @@
}
options.enableUninstantiatedTypeOptimizationForInterfaces = true;
})
- .setMinApi(parameters.getRuntime())
+ .allowDiagnosticWarningMessages(
+ mode == Mode.INVOKE_UNVERIFIABLE_METHOD && !useInterface)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo(
+ "The method `void UnverifiableClass.unverifiableMethod()` does not type check"
+ + " and will be assumed to be unreachable."))
.run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(
r8ResultWithUninstantiatedTypeOptimizationForInterfaces,
diff --git a/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java b/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
index 28e04fd..cd153f2 100644
--- a/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
@@ -44,7 +45,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().build();
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
}
public PreserveDesugaredLambdaTest(TestParameters parameters) {
@@ -58,16 +59,18 @@
testForR8(parameters.getBackend())
.addProgramClasses(Interface.class, A.class)
.addKeepAllClassesRule()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile();
// A is not passed in to ensure the Enqueuer is not tracing through classpath to see the use of
- // computeFoo().s
+ // computeFoo().
testForR8(parameters.getBackend())
.addProgramClasses(Main.class)
.addClasspathClasses(Interface.class)
.addLibraryFiles(TestBase.runtimeJar(parameters.getBackend()))
.addKeepAllClassesRule()
- .setMinApi(parameters.getRuntime())
+ .allowDiagnosticWarningMessages(
+ parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+ .setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryCompileResult.writeToZip())
.inspect(
diff --git a/src/test/java/com/android/tools/r8/shaking/UsageInformationConsumerTest.java b/src/test/java/com/android/tools/r8/shaking/UsageInformationConsumerTest.java
index 4c9b057..3355fab 100644
--- a/src/test/java/com/android/tools/r8/shaking/UsageInformationConsumerTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/UsageInformationConsumerTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
@@ -11,8 +12,6 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.StringUtils;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -49,16 +48,16 @@
@Test
public void testRule() throws Exception {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
testForR8(parameters.getBackend())
- .redirectStdOut(new PrintStream(out))
+ .collectStdout()
.addProgramClasses(TestClass.class, UnusedClass.class)
.addKeepClassAndMembersRules(TestClass.class)
.addKeepRules("-printusage")
.setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertStdoutThatMatches(equalTo(StringUtils.lines(UnusedClass.class.getTypeName())))
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
- assertEquals(StringUtils.lines(UnusedClass.class.getTypeName()), out.toString());
}
static class UnusedClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/VerticalClassMergerInvokeSpecialInConstructorTest.java b/src/test/java/com/android/tools/r8/shaking/VerticalClassMergerInvokeSpecialInConstructorTest.java
new file mode 100644
index 0000000..4f2943f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/VerticalClassMergerInvokeSpecialInConstructorTest.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This is a reproduction of b/148313389, where we rewrite the super-call in B.<init>. */
+@RunWith(Parameterized.class)
+public class VerticalClassMergerInvokeSpecialInConstructorTest extends TestBase {
+
+ public static final String[] EXPECTED =
+ new String[] {"A.<init>", "B.<init>", "A.foo", "C.<init>", "B.foo"};
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public VerticalClassMergerInvokeSpecialInConstructorTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addInnerClasses(VerticalClassMergerInvokeSpecialInConstructorTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(VerticalClassMergerInvokeSpecialInConstructorTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(A.class)
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ public abstract static class A {
+
+ public A() {
+ System.out.println("A.<init>");
+ }
+
+ public void foo() {
+ System.out.println("A.foo");
+ }
+ }
+
+ public static class B extends A {
+
+ public B() {
+ System.out.println("B.<init>");
+ super.foo(); // <-- In b/148313389, this was rewritten to invoke-direct B.foo.
+ }
+
+ @Override
+ public void foo() {
+ System.out.println("B.foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class C extends B {
+
+ public C() {
+ System.out.println("C.<init>");
+ super.foo();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new C();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
index 5c6a305..84ceffa 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -62,7 +63,7 @@
@Parameterized.Parameters(name = "{0} target: {1} minify: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimes().build(),
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
KotlinTargetVersion.values(),
BooleanUtils.values());
}
@@ -87,20 +88,22 @@
@Test
public void b120951621_keepAll() throws Exception {
- CodeInspector inspector = testForR8(parameters.getBackend())
- .addProgramFiles(getKotlinJarFile(FOLDER))
- .addProgramFiles(getJavaJarFile(FOLDER))
- .addKeepMainRule(MAIN_CLASS_NAME)
- .addKeepRules(KEEP_ANNOTATIONS)
- .addKeepRules(
- "-keep @interface " + ANNOTATION_NAME + " {",
- " *;",
- "}"
- )
- .minification(minify)
- .setMinApi(parameters.getRuntime())
- .run(parameters.getRuntime(), MAIN_CLASS_NAME)
- .assertSuccessWithOutput(JAVA_OUTPUT).inspector();
+ CodeInspector inspector =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .addKeepMainRule(MAIN_CLASS_NAME)
+ .addKeepRules(KEEP_ANNOTATIONS)
+ .addKeepRules("-keep @interface " + ANNOTATION_NAME + " {", " *;", "}")
+ .allowDiagnosticWarningMessages()
+ .minification(minify)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+ .assertSuccessWithOutput(JAVA_OUTPUT)
+ .inspector();
ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
assertThat(clazz, isPresent());
assertThat(clazz, not(isRenamed()));
@@ -126,20 +129,25 @@
@Test
public void b120951621_partiallyKeep() throws Exception {
- CodeInspector inspector = testForR8(parameters.getBackend())
- .addProgramFiles(getKotlinJarFile(FOLDER))
- .addProgramFiles(getJavaJarFile(FOLDER))
- .addKeepMainRule(MAIN_CLASS_NAME)
- .addKeepRules(KEEP_ANNOTATIONS)
- .addKeepRules(
- "-keep,allowobfuscation @interface " + ANNOTATION_NAME + " {",
- " java.lang.String *f2();",
- "}"
- )
- .minification(minify)
- .setMinApi(parameters.getRuntime())
- .run(parameters.getRuntime(), MAIN_CLASS_NAME)
- .assertSuccessWithOutput(JAVA_OUTPUT).inspector();
+ CodeInspector inspector =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .addKeepMainRule(MAIN_CLASS_NAME)
+ .addKeepRules(KEEP_ANNOTATIONS)
+ .addKeepRules(
+ "-keep,allowobfuscation @interface " + ANNOTATION_NAME + " {",
+ " java.lang.String *f2();",
+ "}")
+ .allowDiagnosticWarningMessages()
+ .minification(minify)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+ .assertSuccessWithOutput(JAVA_OUTPUT)
+ .inspector();
ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
assertThat(clazz, isPresent());
assertEquals(minify, clazz.isRenamed());
@@ -163,15 +171,21 @@
@Test
public void b120951621_keepAnnotation() throws Exception {
- CodeInspector inspector = testForR8(parameters.getBackend())
- .addProgramFiles(getKotlinJarFile(FOLDER))
- .addProgramFiles(getJavaJarFile(FOLDER))
- .addKeepMainRule(MAIN_CLASS_NAME)
- .addKeepRules(KEEP_ANNOTATIONS)
- .minification(minify)
- .setMinApi(parameters.getRuntime())
- .run(parameters.getRuntime(), MAIN_CLASS_NAME)
- .assertSuccessWithOutput(JAVA_OUTPUT).inspector();
+ CodeInspector inspector =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .addKeepMainRule(MAIN_CLASS_NAME)
+ .addKeepRules(KEEP_ANNOTATIONS)
+ .allowDiagnosticWarningMessages()
+ .minification(minify)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+ .assertSuccessWithOutput(JAVA_OUTPUT)
+ .inspector();
ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
assertThat(clazz, isPresent());
assertEquals(minify, clazz.isRenamed());
@@ -195,14 +209,20 @@
@Test
public void b120951621_noKeep() throws Exception {
- CodeInspector inspector = testForR8(parameters.getBackend())
- .addProgramFiles(getKotlinJarFile(FOLDER))
- .addProgramFiles(getJavaJarFile(FOLDER))
- .addKeepMainRule(MAIN_CLASS_NAME)
- .minification(minify)
- .setMinApi(parameters.getRuntime())
- .run(parameters.getRuntime(), MAIN_CLASS_NAME)
- .assertSuccessWithOutput(OUTPUT_WITHOUT_ANNOTATION).inspector();
+ CodeInspector inspector =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .addKeepMainRule(MAIN_CLASS_NAME)
+ .allowDiagnosticWarningMessages()
+ .minification(minify)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+ .assertSuccessWithOutput(OUTPUT_WITHOUT_ANNOTATION)
+ .inspector();
ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
assertThat(clazz, isPresent());
assertEquals(minify, clazz.isRenamed());
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
index 9e92530..acb1e86 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
@@ -5,11 +5,13 @@
package com.android.tools.r8.shaking.annotations.b137392797;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -70,8 +72,12 @@
"com.squareup.wire.WireField", "com.squareup.demo.myapplication.Test")
.addKeepMainRule(TestClass.class)
.addKeepAttributes("*Annotation*")
- .setMinApi(parameters.getRuntime())
+ .allowDiagnosticWarningMessages(
+ parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+ .setMinApi(parameters.getApiLevel())
.compile()
+ .assertAllWarningMessagesMatch(
+ containsString("required for default or static interface methods desugaring"))
.inspect(this::checkEnumUses)
.run(parameters.getRuntime(), TestClass.class, "com.squareup.demo.myapplication.Test")
.assertSuccessWithOutputLines(
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
index fd2696f..0d9fa5e 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.D8Command;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
@@ -274,11 +275,10 @@
AndroidApiLevel.O_MR1,
AndroidApiLevel.O_MR1,
expectedResultForNative(AndroidApiLevel.O_MR1),
- builder ->
- builder.addOptionsModification(
- options ->
- // android.os.Build$VERSION only exists in the Android runtime.
- options.testing.allowUnusedProguardConfigurationRules = backend == Backend.CF),
+ builder -> {
+ // android.os.Build$VERSION only exists in the Android runtime.
+ builder.allowUnusedProguardConfigurationRules(backend == Backend.CF);
+ },
this::compatCodeNotPresent,
ImmutableList.of(
"-assumevalues class android.os.Build$VERSION { public static final int SDK_INT return "
@@ -307,9 +307,7 @@
AndroidApiLevel.O_MR1,
AndroidApiLevel.O_MR1,
expectedResultForNative(AndroidApiLevel.O_MR1),
- builder ->
- builder.addOptionsModification(
- options -> options.testing.allowUnusedProguardConfigurationRules = true),
+ builder -> builder.allowUnusedProguardConfigurationRules(backend == Backend.CF),
this::compatCodePresent,
ImmutableList.of(rule),
SynthesizedRule.NOT_PRESENT);
@@ -332,9 +330,7 @@
AndroidApiLevel.O_MR1,
AndroidApiLevel.O_MR1,
expectedResultForNative(AndroidApiLevel.O_MR1),
- builder ->
- builder.addOptionsModification(
- options -> options.testing.allowUnusedProguardConfigurationRules = true),
+ R8TestBuilder::allowUnusedProguardConfigurationRules,
this::compatCodeNotPresent,
ImmutableList.of(rule),
SynthesizedRule.PRESENT);
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
index 6d0d767..4ce41ce 100644
--- a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.shaking.b134858535;
+import static org.hamcrest.CoreMatchers.containsString;
+
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -25,7 +27,9 @@
.addProgramClassFileData(EventPublisher$bDump.dump())
.addKeepClassRules(Interface.class)
.addKeepMainRule(Main.class)
+ .allowDiagnosticInfoMessages()
.setMinApi(AndroidApiLevel.L)
- .compile();
+ .compile()
+ .assertAllWarningMessagesMatch(containsString("Unrecognized Kotlin lambda"));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java b/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
index ab37cb0..27bb092 100644
--- a/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
@@ -8,9 +8,14 @@
import com.android.tools.r8.NeverMerge;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
@NeverMerge
interface I {
@@ -34,68 +39,82 @@
}
}
+@RunWith(Parameterized.class)
public class KeepRuleWarningTest extends TestBase {
+
private static final Class<?> MAIN = KeepRuleWarningTestRunner.class;
private static final String EXPECTED_OUTPUT = StringUtils.lines("static::foo", "default::bar");
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.L).build();
+ }
+
+ public KeepRuleWarningTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
@Test
public void test_allMethods() throws Exception {
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addProgramClasses(I.class, C.class, MAIN)
- .setMinApi(AndroidApiLevel.L)
+ .setMinApi(parameters.getApiLevel())
.enableMergeAnnotations()
.addKeepMainRule(MAIN)
.addKeepRules("-keep interface **.I { <methods>; }")
.compile()
.inspectDiagnosticMessages(TestDiagnosticMessages::assertNoMessages)
- .run(MAIN)
+ .run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void test_asterisk() throws Exception {
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addProgramClasses(I.class, C.class, MAIN)
- .setMinApi(AndroidApiLevel.L)
+ .setMinApi(parameters.getApiLevel())
.enableMergeAnnotations()
.addKeepMainRule(MAIN)
.addKeepRules("-keep interface **.I { *(); }")
.compile()
.inspectDiagnosticMessages(TestDiagnosticMessages::assertNoMessages)
- .run(MAIN)
+ .run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void test_stillNotSpecific() throws Exception {
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addProgramClasses(I.class, C.class, MAIN)
- .setMinApi(AndroidApiLevel.L)
+ .setMinApi(parameters.getApiLevel())
.enableMergeAnnotations()
.addKeepMainRule(MAIN)
.addKeepRules("-keep interface **.I { *** f*(); }")
.compile()
.inspectDiagnosticMessages(TestDiagnosticMessages::assertNoMessages)
- .run(MAIN)
+ .run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void test_specific() throws Exception {
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addProgramClasses(I.class, C.class, MAIN)
- .setMinApi(AndroidApiLevel.L)
+ .setMinApi(parameters.getApiLevel())
.enableMergeAnnotations()
.addKeepMainRule(MAIN)
.addKeepRules("-keep interface **.I { static void foo(); }")
+ .allowDiagnosticWarningMessages()
.compile()
- .inspectDiagnosticMessages(m -> {
- m.assertWarningsCount(1)
- .assertWarningMessageThatMatches(containsString("static void foo()"))
- .assertWarningMessageThatMatches(containsString("is ignored"))
- .assertWarningMessageThatMatches(containsString("will be desugared"));
- })
- .run(MAIN)
+ .inspectDiagnosticMessages(
+ m ->
+ m.assertWarningsCount(1)
+ .assertWarningMessageThatMatches(containsString("static void foo()"))
+ .assertWarningMessageThatMatches(containsString("is ignored"))
+ .assertWarningMessageThatMatches(containsString("will be desugared")))
+ .run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
index 8cb1cf0..445e24b 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
@@ -47,6 +47,10 @@
}
private TestShrinkerBuilder<?, ?, ?, ?, ?> getTestBuilder() {
+ return getTestBuilder(false);
+ }
+
+ private TestShrinkerBuilder<?, ?, ?, ?, ?> getTestBuilder(boolean allowDiagnosticInfoMessages) {
switch (shrinker) {
case PROGUARD6:
assertTrue(parameters.isCfRuntime());
@@ -54,7 +58,7 @@
case R8:
return testForR8(parameters.getBackend())
.addTestingAnnotationsAsProgramClasses()
- .allowUnusedProguardConfigurationRules()
+ .allowUnusedProguardConfigurationRules(allowDiagnosticInfoMessages)
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations();
default:
@@ -66,7 +70,7 @@
public void ifOnPublic_noPublicClassForIfRule() throws Exception {
assumeFalse(shrinker.isProguard() && parameters.isDexRuntime());
- getTestBuilder()
+ getTestBuilder(shrinker.isR8())
.addProgramClasses(CLASSES)
.addKeepRules(
"-repackageclasses 'top'",
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
index 54fcadf..b79d90e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic;
import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -17,15 +18,32 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class IfRuleWithInterfaceMethodDesugaringTest extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.M).build();
+ }
+
+ public IfRuleWithInterfaceMethodDesugaringTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
@Test
public void test() throws Exception {
String expectedOutput =
@@ -34,7 +52,7 @@
testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
CodeInspector inspector =
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addInnerClasses(IfRuleWithInterfaceMethodDesugaringTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(
@@ -50,8 +68,9 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableMergeAnnotations()
- .setMinApi(AndroidApiLevel.M)
- .run(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/RemovedClassTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/RemovedClassTestRunner.java
index 37ba854..9edc64d 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/RemovedClassTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/RemovedClassTestRunner.java
@@ -5,20 +5,19 @@
import static com.android.tools.r8.references.Reference.classFromClass;
import static com.android.tools.r8.references.Reference.methodFromMethod;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.graphinspector.GraphInspector;
import com.android.tools.r8.utils.graphinspector.GraphInspector.QueryNode;
import com.google.common.collect.ImmutableList;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.nio.charset.StandardCharsets;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,15 +33,15 @@
private static final String EXPECTED = StringUtils.lines("called bar");
- private final Backend backend;
+ private final TestParameters parameters;
@Parameters(name = "{0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public RemovedClassTestRunner(Backend backend) {
- this.backend = backend;
+ public RemovedClassTestRunner(TestParameters parameters) {
+ this.parameters = parameters;
}
@Test
@@ -51,22 +50,24 @@
MethodReference barMethod = methodFromMethod(CLASS.getDeclaredMethod("bar"));
MethodReference bazMethod = methodFromMethod(CLASS.getDeclaredMethod("baz"));
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
R8TestCompileResult compileResult =
- testForR8(backend)
+ testForR8(parameters.getBackend())
.enableGraphInspector()
.enableInliningAnnotations()
.addProgramClasses(CLASSES)
.addKeepMethodRules(mainMethod)
.addKeepRules("-whyareyoukeeping class " + REMOVED_CLASS.getTypeName())
- .redirectStdOut(new PrintStream(baos))
- .compile();
- String expectedOutput = StringUtils.lines("Nothing is keeping " + REMOVED_CLASS.getTypeName());
- String compileOutput = new String(baos.toByteArray(), StandardCharsets.UTF_8);
- assertEquals(expectedOutput, compileOutput);
+ .setMinApi(parameters.getApiLevel())
+ .collectStdout()
+ .compile()
+ .assertStdoutThatMatches(
+ equalTo(StringUtils.lines("Nothing is keeping " + REMOVED_CLASS.getTypeName())));
GraphInspector inspector =
- compileResult.run(CLASS).assertSuccessWithOutput(EXPECTED).graphInspector();
+ compileResult
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .graphInspector();
// The only root should be the keep main-method rule.
assertEquals(1, inspector.getRoots().size());
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index f4ed50c..2387e7c 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -5,7 +5,6 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -13,7 +12,6 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.StringUtils;
import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;
@@ -40,7 +38,7 @@
return getTestParameters().withNoneRuntime().build();
}
- final TestParameters parameters;
+ private final TestParameters parameters;
public WhyAreYouKeepingAllTest(TestParameters parameters) {
this.parameters = parameters;
@@ -53,13 +51,13 @@
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
.addKeepRules(WHY_ARE_YOU_KEEPING_ALL)
- .redirectStdOut(new PrintStream(baos))
- .compile();
- assertThat(baos.toString(), containsString("referenced in keep rule"));
-
- // TODO(b/124655065): We should always know the reason for keeping.
- // It is OK if this starts failing while the kept-graph API is incomplete, in which case replace
- // the 'not(containsString(' by just 'containsString('.
- assertThat(baos.toString(), not(containsString("kept for unknown reasons")));
+ .collectStdout()
+ .compile()
+ .assertStdoutThatMatches(containsString("referenced in keep rule"))
+ // TODO(b/124655065): We should always know the reason for keeping.
+ // It is OK if this starts failing while the kept-graph API is incomplete, in which case
+ // replace
+ // the 'not(containsString(' by just 'containsString('.
+ .assertStdoutThatMatches(not(containsString("kept for unknown reasons")));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
index 82234e7..a084679 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
@@ -5,12 +5,14 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -30,31 +32,38 @@
private static final Path MOCKITO_INTERFACE_JAR =
Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, M_I_PKG + FileUtils.JAR_EXTENSION);
- private final Backend backend;
+ private final TestParameters parameters;
private final boolean minify;
- @Parameterized.Parameters(name = "Backend: {0} minify: {1}")
+ @Parameterized.Parameters(name = "{0}, minify: {1}")
public static Collection<Object[]> data() {
- return buildParameters(ToolHelper.getBackends(), BooleanUtils.values());
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
- public MockitoTest(Backend backend, boolean minify) {
- this.backend = backend;
+ public MockitoTest(TestParameters parameters, boolean minify) {
+ this.parameters = parameters;
this.minify = minify;
}
@Test
public void b120675359_devirtualized() throws Exception {
Path flagToKeepTestRunner = Paths.get(ToolHelper.EXAMPLES_DIR, M_I_PKG, "keep-rules.txt");
- R8FullTestBuilder builder =
- testForR8(backend)
+ CodeInspector inspector =
+ testForR8(parameters.getBackend())
.addProgramFiles(MOCKITO_INTERFACE_JAR)
.addKeepRuleFiles(flagToKeepTestRunner)
- .minification(minify);
- CodeInspector inspector = builder.compile().inspector();
+ .allowDiagnosticWarningMessages(
+ parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+ .minification(minify)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString("required for default or static interface methods desugaring"))
+ .inspector();
ClassSubject itf = inspector.clazz(M_I);
assertThat(itf, isPresent());
- MethodSubject mtd = itf.method("void", "onEnterForeground");
+ MethodSubject mtd = itf.uniqueMethodWithName("onEnterForeground");
assertThat(mtd, not(isPresent()));
}
@@ -62,15 +71,21 @@
public void b120675359_conditional_keep() throws Exception {
Path flagToKeepInterfaceConditionally =
Paths.get(ToolHelper.EXAMPLES_DIR, M_I_PKG, "keep-rules-conditional-on-mock.txt");
- R8FullTestBuilder builder =
- testForR8(backend)
+ CodeInspector inspector =
+ testForR8(parameters.getBackend())
.addProgramFiles(MOCKITO_INTERFACE_JAR)
.addKeepRuleFiles(flagToKeepInterfaceConditionally)
- .minification(minify);
- CodeInspector inspector = builder.compile().inspector();
+ .allowDiagnosticWarningMessages(
+ parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+ .minification(minify)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString("required for default or static interface methods desugaring"))
+ .inspector();
ClassSubject itf = inspector.clazz(M_I);
assertThat(itf, isPresent());
- MethodSubject mtd = itf.method("void", "onEnterForeground");
+ MethodSubject mtd = itf.uniqueMethodWithName("onEnterForeground");
assertThat(mtd, isPresent());
assertThat(mtd, not(isRenamed()));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingOverriddenMethodTest.java b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingOverriddenMethodTest.java
index 2914c4e..39d3aaf 100644
--- a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingOverriddenMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingOverriddenMethodTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
@@ -45,7 +46,6 @@
private void testViaConfig(Class<?> main, Class<?> targetClass, Class<?> subClass)
throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
testForR8(Backend.DEX)
.addInnerClasses(WhyAreYouKeepingOverriddenMethodTest.class)
.addKeepMainRule(main)
@@ -55,13 +55,12 @@
.enableInliningAnnotations()
.enableMergeAnnotations()
.minification(minification)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(AndroidApiLevel.B)
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
- .redirectStdOut(new PrintStream(baos))
- .compile();
- String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
- assertThat(output, containsString(expectedMessage(targetClass)));
- assertThat(output, not(containsString(expectedNotContainingMessage(subClass))));
+ .collectStdout()
+ .compile()
+ .assertStdoutThatMatches(containsString(expectedMessage(targetClass)))
+ .assertStdoutThatMatches(not(containsString(expectedNotContainingMessage(subClass))));
}
private void testViaConsumer(
@@ -74,7 +73,7 @@
.enableInliningAnnotations()
.enableMergeAnnotations()
.minification(minification)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(AndroidApiLevel.B)
.setKeptGraphConsumer(graphConsumer)
.compile();
diff --git a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
index c230c00..eeb280b 100644
--- a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
@@ -4,8 +4,9 @@
package com.android.tools.r8.shaking.whyareyoukeeping;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverInline;
@@ -77,10 +78,9 @@
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
.addKeepRules("-whyareyoukeeping class " + A.class.getTypeName())
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
- .redirectStdOut(new PrintStream(baos))
- .compile();
- String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
- assertEquals(expected, output);
+ .collectStdout()
+ .compile()
+ .assertStdoutThatMatches(equalTo(expected));
}
@Test
@@ -108,10 +108,9 @@
.addKeepRules("-whyareyoukeeping class " + A.class.getTypeName() + " { baz(); }")
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
- .redirectStdOut(new PrintStream(baos))
- .compile();
- String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
- assertEquals(expected + expectedPathToBaz, output);
+ .collectStdout()
+ .compile()
+ .assertStdoutThatMatches(equalTo(expected + expectedPathToBaz));
}
@Test
@@ -133,18 +132,15 @@
@Test
public void testNonExistentClassWhyAreYouKeepingViaProguardConfig() throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
testForR8(backend)
.addProgramClasses(A.class)
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
.addKeepRules("-whyareyoukeeping class NonExistentClass")
.allowUnusedProguardConfigurationRules()
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
- .redirectStdOut(new PrintStream(baos))
- .compile();
- String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
- // Expected outcome is empty.
- assertEquals("", output);
+ .collectStdout()
+ .compile()
+ .assertNoStdout();
}
@Test
@@ -156,10 +152,8 @@
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
.addKeepRules("-whyareyoukeeping class " + aName + " { nonExistentMethod(); }")
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
- .redirectStdOut(new PrintStream(baos))
- .compile();
- String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
- // Expected outcome is empty.
- assertFalse("b/122820741", output.isEmpty());
+ .collectStdout()
+ .compile()
+ .assertStdoutThatMatches(not(equalTo("")));
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/ForwardingOutputStream.java b/src/test/java/com/android/tools/r8/utils/ForwardingOutputStream.java
new file mode 100644
index 0000000..082c947
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ForwardingOutputStream.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+
+public class ForwardingOutputStream extends OutputStream {
+
+ private final List<OutputStream> listeners;
+
+ public ForwardingOutputStream(OutputStream... listeners) {
+ this.listeners = Arrays.asList(listeners);
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ for (OutputStream out : listeners) {
+ out.write(b);
+ }
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ for (OutputStream out : listeners) {
+ out.write(b);
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ for (OutputStream out : listeners) {
+ out.write(b, off, len);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ for (OutputStream out : listeners) {
+ out.flush();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ for (OutputStream out : listeners) {
+ out.close();
+ }
+ }
+}
diff --git a/tools/apk_utils.py b/tools/apk_utils.py
index 69a9db9..9155657 100644
--- a/tools/apk_utils.py
+++ b/tools/apk_utils.py
@@ -10,7 +10,7 @@
def sign(unsigned_apk, signed_apk, keystore, quiet=False, logging=True):
utils.Print('Signing (ignore the warnings)', quiet=quiet)
cmd = ['zip', '-d', unsigned_apk, 'META-INF/*']
- utils.RunCmd(cmd, quiet=quiet, logging=logging)
+ utils.RunCmd(cmd, quiet=quiet, logging=logging, fail=False)
cmd = [
'jarsigner',
'-sigalg', 'SHA1withRSA',
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py
index 29f7459..c035520 100755
--- a/tools/build_sample_apk.py
+++ b/tools/build_sample_apk.py
@@ -42,7 +42,8 @@
result.add_option('--api',
help='Android api level',
default=21,
- choices=[14, 15, 19, 21, 22, 23, 24, 25, 26])
+ choices=['14', '15', '19', '21', '22', '23', '24', '25',
+ '26'])
result.add_option('--keystore',
help='Keystore used for signing',
default=DEFAULT_KEYSTORE)
@@ -156,7 +157,8 @@
'--classpath', utils.get_android_jar(api),
'--min-api', str(api)]
command.extend(files)
- command.append(get_guava_jar())
+ if app != 'simple':
+ command.append(get_guava_jar())
utils.PrintCmd(command)
subprocess.check_call(command)
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index c31bfea..ab01595 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -163,6 +163,10 @@
help='Setting the increment',
type='int',
default=32)
+ result.add_option('--print-times',
+ help='Include timing',
+ default=False,
+ action='store_true')
return result.parse_args(argv)
@@ -431,6 +435,9 @@
if not options.ignore_java_version:
utils.check_java_version()
+ if options.print_times:
+ extra_args.append('-Dcom.android.tools.r8.printtimes=1')
+
outdir = options.out
(version_id, data) = get_version_and_data(options)