Merge "Revert "The meet of precise types is only defined for identical types.""
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 805bdf7..fd547cf 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -235,7 +235,7 @@
System.setOut(new PrintStream(ByteStreams.nullOutputStream()));
}
// TODO(b/65390962): Remove this warning once the CF backend is complete.
- if (options.isGeneratingClassFiles()) {
+ if (options.isGeneratingClassFiles() && !options.testing.suppressExperimentalCfBackendWarning) {
options.reporter.warning(new StringDiagnostic(
"R8 support for generating Java classfiles is incomplete and experimental. "
+ "Even if R8 appears to succeed, the generated output is likely incorrect."));
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 584b06d..f0ae9e0 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -618,6 +618,13 @@
internal.proguardCompatibilityRulesOutput = proguardCompatibilityRulesOutput;
internal.dataResourceConsumer = internal.programConsumer.getDataResourceConsumer();
+ // Default is to remove Java assertion code as Dalvik and Art does not reliable support
+ // Java assertions. When generation class file output always keep the Java assertions code.
+ assert internal.disableAssertions;
+ if (internal.isGeneratingClassFiles()) {
+ internal.disableAssertions = false;
+ }
+
// EXPERIMENTAL flags.
assert !internal.forceProguardCompatibility;
internal.forceProguardCompatibility = forceProguardCompatibility;
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 55c4134..0ef8a34 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -489,17 +489,22 @@
}
public boolean canTriggerStaticInitializer(DexType type, boolean ignoreTypeItself) {
+ DexClass clazz = definitionFor(type);
+ assert clazz != null;
+ return canTriggerStaticInitializer(clazz, ignoreTypeItself);
+ }
+
+ public boolean canTriggerStaticInitializer(DexClass clazz, boolean ignoreTypeItself) {
Set<DexType> knownInterfaces = Sets.newIdentityHashSet();
// Process superclass chain.
- DexType clazz = type;
- while (clazz != null && clazz != dexItemFactory.objectType) {
- DexClass definition = definitionFor(clazz);
- if (canTriggerStaticInitializer(definition) && (!ignoreTypeItself || clazz != type)) {
+ DexClass current = clazz;
+ while (current != null && current.type != dexItemFactory.objectType) {
+ if (canTriggerStaticInitializer(current) && (!ignoreTypeItself || current != clazz)) {
return true;
}
- knownInterfaces.addAll(Arrays.asList(definition.interfaces.values));
- clazz = definition.superType;
+ knownInterfaces.addAll(Arrays.asList(current.interfaces.values));
+ current = current.superType != null ? definitionFor(current.superType) : null;
}
// Process interfaces.
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 64c85f3..fc1ef7b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Iterators;
import java.util.Arrays;
@@ -138,16 +137,6 @@
}
}
- public <E extends Throwable> void forEachMethodThrowing(
- ThrowingConsumer<DexEncodedMethod, E> consumer) throws E {
- for (DexEncodedMethod method : directMethods()) {
- consumer.accept(method);
- }
- for (DexEncodedMethod method : virtualMethods()) {
- consumer.accept(method);
- }
- }
-
public DexEncodedMethod[] allMethodsSorted() {
int vLen = virtualMethods.length;
int dLen = directMethods.length;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 3586d6d..9a44dde 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -634,6 +634,7 @@
// class inliner, null value indicates that the method is not eligible.
private ClassInlinerEligibility classInlinerEligibility = null;
private TrivialInitializer trivialInitializerInfo = null;
+ private boolean initializerEnablingJavaAssertions = false;
private ParameterUsagesInfo parametersUsages = null;
private BitSet kotlinNotNullParamHints = null;
@@ -705,6 +706,14 @@
return this.trivialInitializerInfo;
}
+ private void setInitializerEnablingJavaAssertions() {
+ this.initializerEnablingJavaAssertions = true;
+ }
+
+ public boolean isInitializerEnablingJavaAssertions() {
+ return initializerEnablingJavaAssertions;
+ }
+
public long getReturnedConstant() {
assert returnsConstant();
return returnedConstant;
@@ -754,6 +763,10 @@
forceInline = true;
}
+ private void unsetForceInline() {
+ forceInline = false;
+ }
+
private void markPublicized() {
publicized = true;
}
@@ -831,10 +844,18 @@
ensureMutableOI().setTrivialInitializer(info);
}
+ synchronized public void setInitializerEnablingJavaAssertions() {
+ ensureMutableOI().setInitializerEnablingJavaAssertions();
+ }
+
synchronized public void markForceInline() {
ensureMutableOI().markForceInline();
}
+ public synchronized void unsetForceInline() {
+ ensureMutableOI().unsetForceInline();
+ }
+
synchronized public void markPublicized() {
ensureMutableOI().markPublicized();
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 64ba508..e998206 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -27,15 +27,21 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.FieldSignatureEquivalence;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.base.Equivalence.Wrapper;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.objectweb.asm.AnnotationVisitor;
@@ -148,8 +154,10 @@
private List<DexAnnotationElement> defaultAnnotations = null;
private final List<DexEncodedField> staticFields = new ArrayList<>();
private final List<DexEncodedField> instanceFields = new ArrayList<>();
+ private final Set<Wrapper<DexField>> fieldSignatures = new HashSet<>();
private final List<DexEncodedMethod> directMethods = new ArrayList<>();
private final List<DexEncodedMethod> virtualMethods = new ArrayList<>();
+ private final Set<Wrapper<DexMethod>> methodSignatures = new HashSet<>();
public CreateDexClassVisitor(
Origin origin,
@@ -380,13 +388,20 @@
public void visitEnd() {
FieldAccessFlags flags = FieldAccessFlags.fromCfAccessFlags(cleanAccessFlags(access));
DexField dexField = parent.application.getField(parent.type, name, desc);
- DexAnnotationSet annotationSet = createAnnotationSet(annotations);
- DexValue staticValue = flags.isStatic() ? getStaticValue(value, dexField.type) : null;
- DexEncodedField field = new DexEncodedField(dexField, flags, annotationSet, staticValue);
- if (flags.isStatic()) {
- parent.staticFields.add(field);
+ Wrapper<DexField> signature = FieldSignatureEquivalence.get().wrap(dexField);
+ if (parent.fieldSignatures.add(signature)) {
+ DexAnnotationSet annotationSet = createAnnotationSet(annotations);
+ DexValue staticValue = flags.isStatic() ? getStaticValue(value, dexField.type) : null;
+ DexEncodedField field = new DexEncodedField(dexField, flags, annotationSet, staticValue);
+ if (flags.isStatic()) {
+ parent.staticFields.add(field);
+ } else {
+ parent.instanceFields.add(field);
+ }
} else {
- parent.instanceFields.add(field);
+ parent.application.options.reporter.warning(
+ new StringDiagnostic(
+ String.format("Field `%s` has multiple definitions", dexField.toSourceString())));
}
}
@@ -603,10 +618,20 @@
annotationsList,
code,
parent.version);
- if (flags.isStatic() || flags.isConstructor() || flags.isPrivate()) {
- parent.directMethods.add(dexMethod);
+ Wrapper<DexMethod> signature = MethodSignatureEquivalence.get().wrap(method);
+ if (parent.methodSignatures.add(signature)) {
+ if (flags.isStatic() || flags.isConstructor() || flags.isPrivate()) {
+ parent.directMethods.add(dexMethod);
+ } else {
+ parent.virtualMethods.add(dexMethod);
+ }
} else {
- parent.virtualMethods.add(dexMethod);
+ internalOptions.reporter.warning(
+ new StringDiagnostic(
+ String.format(
+ "Ignoring an implementation of the method `%s` because it has multiple "
+ + "definitions",
+ method.toSourceString())));
}
if (defaultAnnotation != null) {
parent.addDefaultAnnotation(name, defaultAnnotation);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index 6b3c89c..3b1b2d1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -34,6 +34,14 @@
return inValues.get(0);
}
+ public boolean isEnter() {
+ return type == Type.ENTER;
+ }
+
+ public boolean isExit() {
+ return type == Type.EXIT;
+ }
+
@Override
public void buildDex(DexBuilder builder) {
// If the monitor object is an argument, we use the argument register for all the monitor
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index 650757e..382dd64 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -15,16 +16,23 @@
import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.ThrowingBiConsumer;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -56,7 +64,7 @@
this.shuffle = options.testing.irOrdering;
}
- private static class Node {
+ public static class Node {
public final DexEncodedMethod method;
private int invokeCount = 0;
@@ -68,7 +76,7 @@
// Incoming calls to this method.
private final Set<Node> callers = new LinkedHashSet<>();
- private Node(DexEncodedMethod method) {
+ public Node(DexEncodedMethod method) {
this.method = method;
}
@@ -76,19 +84,20 @@
return method.accessFlags.isBridge();
}
- private void addCallee(Node method) {
+ public void addCallee(Node method) {
callees.add(method);
+ method.callers.add(this);
}
- private void addCaller(Node method) {
- callers.add(method);
+ public boolean hasCallee(Node method) {
+ return callees.contains(method);
}
boolean isSelfRecursive() {
return isSelfRecursive;
}
- boolean isLeaf() {
+ public boolean isLeaf() {
return callees.isEmpty();
}
@@ -96,7 +105,7 @@
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("MethodNode for: ");
- builder.append(method.qualifiedName());
+ builder.append(method.toSourceString());
builder.append(" (");
builder.append(callees.size());
builder.append(" callees, ");
@@ -114,7 +123,7 @@
builder.append("Callees:\n");
for (Node call : callees) {
builder.append(" ");
- builder.append(call.method.qualifiedName());
+ builder.append(call.method.toSourceString());
builder.append("\n");
}
}
@@ -122,7 +131,7 @@
builder.append("Callers:\n");
for (Node caller : callers) {
builder.append(" ");
- builder.append(caller.method.qualifiedName());
+ builder.append(caller.method.toSourceString());
builder.append("\n");
}
}
@@ -136,8 +145,12 @@
private final Set<DexEncodedMethod> singleCallSite = Sets.newIdentityHashSet();
private final Set<DexEncodedMethod> doubleCallSite = Sets.newIdentityHashSet();
- public static CallGraph build(DexApplication application, AppInfoWithLiveness appInfo,
- GraphLense graphLense, InternalOptions options) {
+ public static CallGraph build(
+ DexApplication application,
+ AppInfoWithLiveness appInfo,
+ GraphLense graphLense,
+ InternalOptions options,
+ Timing timing) {
CallGraph graph = new CallGraph(options);
DexClass[] classes = application.classes().toArray(new DexClass[application.classes().size()]);
Arrays.sort(classes, (DexClass a, DexClass b) -> a.type.slowCompareTo(b.type));
@@ -149,8 +162,13 @@
}
}
assert allMethodsExists(application, graph);
- graph.breakCycles();
- assert graph.breakCycles() == 0; // This time the cycles should be gone.
+
+ timing.begin("Cycle elimination");
+ CycleEliminator cycleEliminator = new CycleEliminator(graph.nodes.values(), options);
+ cycleEliminator.breakCycles();
+ timing.end();
+ assert cycleEliminator.breakCycles() == 0; // This time the cycles should be gone.
+
graph.fillCallSiteSets(appInfo);
return graph;
}
@@ -196,9 +214,10 @@
/**
* Extract the next set of leaves (nodes with an call (outgoing) degree of 0) if any.
- * <p>
- * All nodes in the graph are extracted if called repeatedly until null is returned.
- * Please note that there are no cycles in this graph (see {@link #breakCycles}).
+ *
+ * <p>All nodes in the graph are extracted if called repeatedly until null is returned. Please
+ * note that there are no cycles in this graph (see {@link CycleEliminator#breakCycles}).
+ *
* <p>
*/
private Set<DexEncodedMethod> extractLeaves() {
@@ -215,48 +234,197 @@
.collect(Collectors.toCollection(LinkedHashSet::new)));
}
- private int traverse(Node node, Set<Node> stack, Set<Node> marked) {
- int numberOfCycles = 0;
- if (!marked.contains(node)) {
- assert !stack.contains(node);
- stack.add(node);
- ArrayList<Node> toBeRemoved = null;
- // Sort the callees before calling traverse recursively.
- // This will ensure cycles are broken the same way across
- // multiple invocations of the R8 compiler.
+ public static class CycleEliminator {
+
+ public static final String CYCLIC_FORCE_INLINING_MESSAGE =
+ "Unable to satisfy force inlining constraints due to cyclic force inlining";
+
+ private static class CallEdge {
+
+ private final Node caller;
+ private final Node callee;
+
+ public CallEdge(Node caller, Node callee) {
+ this.caller = caller;
+ this.callee = callee;
+ }
+ }
+
+ private final Collection<Node> nodes;
+ private final InternalOptions options;
+
+ // DFS stack.
+ private Deque<Node> stack = new ArrayDeque<>();
+
+ // Set of nodes on the DFS stack.
+ private Set<Node> stackSet = Sets.newIdentityHashSet();
+
+ // Set of nodes that have been visited entirely.
+ private Set<Node> marked = Sets.newIdentityHashSet();
+
+ private int numberOfCycles = 0;
+
+ public CycleEliminator(Collection<Node> nodes, InternalOptions options) {
+ this.options = options;
+
+ // Call to reorderNodes must happen after assigning options.
+ this.nodes =
+ options.testing.nondeterministicCycleElimination
+ ? reorderNodes(new ArrayList<>(nodes))
+ : nodes;
+ }
+
+ public int breakCycles() {
+ // Break cycles in this call graph by removing edges causing cycles.
+ for (Node node : nodes) {
+ traverse(node);
+ }
+ int result = numberOfCycles;
+ reset();
+ return result;
+ }
+
+ private void reset() {
+ assert stack.isEmpty();
+ assert stackSet.isEmpty();
+ marked.clear();
+ numberOfCycles = 0;
+ }
+
+ private void traverse(Node node) {
+ if (marked.contains(node)) {
+ // Already visited all nodes that can be reached from this node.
+ return;
+ }
+
+ push(node);
+
+ // Sort the callees before calling traverse recursively. This will ensure cycles are broken
+ // the same way across multiple invocations of the R8 compiler.
Node[] callees = node.callees.toArray(new Node[node.callees.size()]);
Arrays.sort(callees, (Node a, Node b) -> a.method.method.slowCompareTo(b.method.method));
+ if (options.testing.nondeterministicCycleElimination) {
+ reorderNodes(Arrays.asList(callees));
+ }
+
for (Node callee : callees) {
- if (stack.contains(callee)) {
- if (toBeRemoved == null) {
- toBeRemoved = new ArrayList<>();
+ if (stackSet.contains(callee)) {
+ // Found a cycle that needs to be eliminated.
+ numberOfCycles++;
+
+ if (edgeRemovalIsSafe(node, callee)) {
+ // Break the cycle by removing the edge node->callee.
+ callee.callers.remove(node);
+ node.callees.remove(callee);
+
+ if (Log.ENABLED) {
+ Log.info(
+ CallGraph.class,
+ "Removed call edge from method '%s' to '%s'",
+ node.method.toSourceString(),
+ callee.method.toSourceString());
+ }
+ } else {
+ // The cycle has a method that is marked as force inline.
+ LinkedList<Node> cycle = extractCycle(callee);
+
+ if (Log.ENABLED) {
+ Log.info(
+ CallGraph.class, "Extracted cycle to find an edge that can safely be removed");
+ }
+
+ // Break the cycle by finding an edge that can be removed without breaking force
+ // inlining. If that is not possible, this call fails with a compilation error.
+ CallEdge edge = findCallEdgeForRemoval(cycle);
+
+ // The edge will be null if this cycle has already been eliminated as a result of
+ // another cycle elimination.
+ if (edge != null) {
+ assert edgeRemovalIsSafe(edge.caller, edge.callee);
+
+ // Break the cycle by removing the edge caller->callee.
+ edge.caller.callees.remove(edge.callee);
+ edge.callee.callers.remove(edge.caller);
+
+ if (Log.ENABLED) {
+ Log.info(
+ CallGraph.class,
+ "Removed call edge from force inlined method '%s' to '%s' to ensure that "
+ + "force inlining will succeed",
+ node.method.toSourceString(),
+ callee.method.toSourceString());
+ }
+ }
+
+ // Recover the stack.
+ recoverStack(cycle);
}
- // We have a cycle; break it by removing node->callee.
- toBeRemoved.add(callee);
- callee.callers.remove(node);
} else {
- numberOfCycles += traverse(callee, stack, marked);
+ traverse(callee);
}
}
- if (toBeRemoved != null) {
- numberOfCycles += toBeRemoved.size();
- node.callees.removeAll(toBeRemoved);
- }
- stack.remove(node);
+ pop(node);
marked.add(node);
}
- return numberOfCycles;
- }
- private int breakCycles() {
- // Break cycles in this call graph by removing edges causing cycles.
- int numberOfCycles = 0;
- Set<Node> stack = Sets.newIdentityHashSet();
- Set<Node> marked = Sets.newIdentityHashSet();
- for (Node node : nodes.values()) {
- numberOfCycles += traverse(node, stack, marked);
+ private void push(Node node) {
+ stack.push(node);
+ boolean changed = stackSet.add(node);
+ assert changed;
}
- return numberOfCycles;
+
+ private void pop(Node node) {
+ Node popped = stack.pop();
+ assert popped == node;
+ boolean changed = stackSet.remove(node);
+ assert changed;
+ }
+
+ private LinkedList<Node> extractCycle(Node entry) {
+ LinkedList<Node> cycle = new LinkedList<>();
+ do {
+ assert !stack.isEmpty();
+ cycle.add(stack.pop());
+ } while (cycle.getLast() != entry);
+ return cycle;
+ }
+
+ private CallEdge findCallEdgeForRemoval(LinkedList<Node> extractedCycle) {
+ Node callee = extractedCycle.getLast();
+ for (Node caller : extractedCycle) {
+ if (!caller.callees.contains(callee)) {
+ // No need to break any edges since this cycle has already been broken previously.
+ assert !callee.callers.contains(caller);
+ return null;
+ }
+ if (edgeRemovalIsSafe(caller, callee)) {
+ return new CallEdge(caller, callee);
+ }
+ callee = caller;
+ }
+ throw new CompilationError(CYCLIC_FORCE_INLINING_MESSAGE);
+ }
+
+ private static boolean edgeRemovalIsSafe(Node caller, Node callee) {
+ // All call edges where the callee is a method that should be force inlined must be kept,
+ // to guarantee that the IR converter will process the callee before the caller.
+ return !callee.method.getOptimizationInfo().forceInline();
+ }
+
+ private void recoverStack(LinkedList<Node> extractedCycle) {
+ Iterator<Node> descendingIt = extractedCycle.descendingIterator();
+ while (descendingIt.hasNext()) {
+ stack.push(descendingIt.next());
+ }
+ }
+
+ private Collection<Node> reorderNodes(List<Node> nodes) {
+ assert options.testing.nondeterministicCycleElimination;
+ if (!InternalOptions.DETERMINISTIC_DEBUGGING) {
+ Collections.shuffle(nodes);
+ }
+ return nodes;
+ }
}
synchronized private Node ensureMethodNode(DexEncodedMethod method) {
@@ -268,7 +436,6 @@
assert callee != null;
if (caller != callee) {
caller.addCallee(callee);
- callee.addCaller(caller);
} else {
caller.isSelfRecursive = true;
}
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 d5f923e..33531da 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
@@ -108,6 +108,7 @@
private final boolean enableWholeProgramOptimizations;
private final OptimizationFeedback ignoreOptimizationFeedback = new OptimizationFeedbackIgnore();
+ private final OptimizationFeedback simpleOptimizationFeedback = new OptimizationFeedbackSimple();
private DexString highestSortingString;
private IRConverter(
@@ -176,7 +177,8 @@
}
this.classInliner =
(options.enableClassInlining && options.enableInlining && inliner != null)
- ? new ClassInliner(appInfo.dexItemFactory, options.classInliningInstructionLimit)
+ ? new ClassInliner(
+ appInfo.dexItemFactory, lambdaRewriter, options.classInliningInstructionLimit)
: null;
}
@@ -356,24 +358,34 @@
ExecutorService executor) throws ExecutionException {
List<Future<?>> futures = new ArrayList<>();
for (DexProgramClass clazz : classes) {
- futures.add(
- executor.submit(
- () -> {
- clazz.forEachMethodThrowing(this::convertMethodToDex);
- return null; // we want a Callable not a Runnable to be able to throw
- }));
+ futures.add(executor.submit(() -> convertMethodsToDex(clazz)));
}
ThreadUtils.awaitFutures(futures);
}
- void convertMethodToDex(DexEncodedMethod method) {
+ private void convertMethodsToDex(DexProgramClass clazz) {
+ // When converting all methods on a class always convert <clinit> first.
+ for (DexEncodedMethod method : clazz.directMethods()) {
+ if (method.isClassInitializer()) {
+ convertMethodToDex(method);
+ break;
+ }
+ }
+ clazz.forEachMethod(method -> {
+ if (!method.isClassInitializer()) {
+ convertMethodToDex(method);
+ }
+ });
+ }
+
+ private void convertMethodToDex(DexEncodedMethod method) {
assert options.isGeneratingDex();
if (method.getCode() != null) {
boolean matchesMethodFilter = options.methodMatchesFilter(method);
if (matchesMethodFilter) {
if (!(options.passthroughDexCode && method.getCode().isDexCode())) {
// We do not process in call graph order, so anything could be a leaf.
- rewriteCode(method, ignoreOptimizationFeedback, x -> true, CallSiteInformation.empty(),
+ rewriteCode(method, simpleOptimizationFeedback, x -> true, CallSiteInformation.empty(),
Outliner::noProcessing);
}
updateHighestSortingStrings(method);
@@ -405,8 +417,8 @@
OptimizationFeedback directFeedback = new OptimizationFeedbackDirect();
{
timing.begin("Build call graph");
- CallGraph callGraph = CallGraph
- .build(application, appInfo.withLiveness(), graphLense, options);
+ CallGraph callGraph =
+ CallGraph.build(application, appInfo.withLiveness(), graphLense, options, timing);
timing.end();
timing.begin("IR conversion phase 1");
BiConsumer<IRCode, DexEncodedMethod> outlineHandler =
@@ -565,7 +577,7 @@
try {
codeRewriter.enterCachedClass(clazz);
// Process the generated class, but don't apply any outlining.
- clazz.forEachMethodThrowing(this::optimizeSynthesizedMethod);
+ clazz.forEachMethod(this::optimizeSynthesizedMethod);
} finally {
codeRewriter.leaveCachedClass(clazz);
}
@@ -672,7 +684,7 @@
codeRewriter.removeSwitchMaps(code);
}
if (options.disableAssertions) {
- codeRewriter.disableAssertions(code);
+ codeRewriter.disableAssertions(appInfo, method, code, feedback);
}
if (options.enableNonNullTracking && nonNullTracker != null) {
nonNullTracker.addNonNull(code);
@@ -733,11 +745,6 @@
assert code.isConsistentSSA();
}
- if (interfaceMethodRewriter != null) {
- interfaceMethodRewriter.rewriteMethodReferences(method, code);
- assert code.isConsistentSSA();
- }
-
if (classInliner != null) {
// Class inliner should work before lambda merger, so if it inlines the
// lambda, it is not get collected by merger.
@@ -755,6 +762,11 @@
assert code.isConsistentSSA();
}
+ if (interfaceMethodRewriter != null) {
+ interfaceMethodRewriter.rewriteMethodReferences(method, code);
+ assert code.isConsistentSSA();
+ }
+
if (lambdaMerger != null) {
lambdaMerger.processMethodCode(method, code);
assert code.isConsistentSSA();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
index c26aecf..49bfac8 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
@@ -21,6 +21,7 @@
void markTriggerClassInitBeforeAnySideEffect(DexEncodedMethod method, boolean mark);
void setClassInlinerEligibility(DexEncodedMethod method, ClassInlinerEligibility eligibility);
void setTrivialInitializer(DexEncodedMethod method, TrivialInitializer info);
+ void setInitializerEnablingJavaAssertions(DexEncodedMethod method);
void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo);
void setKotlinNotNullParamHints(DexEncodedMethod method, BitSet hints);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java
index 2b1a6db..1d7a2d6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java
@@ -60,6 +60,11 @@
}
@Override
+ public void setInitializerEnablingJavaAssertions(DexEncodedMethod method) {
+ method.setInitializerEnablingJavaAssertions();
+ }
+
+ @Override
public void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo) {
method.setParameterUsages(parameterUsagesInfo);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
index a547b36..2fb2969 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
@@ -44,6 +44,10 @@
}
@Override
+ public void setInitializerEnablingJavaAssertions(DexEncodedMethod method) {
+ }
+
+ @Override
public void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo) {
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java
new file mode 100644
index 0000000..b31f28a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
+import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
+import com.android.tools.r8.graph.ParameterUsagesInfo;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
+import java.util.BitSet;
+
+public class OptimizationFeedbackSimple implements OptimizationFeedback {
+
+ @Override
+ public void methodReturnsArgument(DexEncodedMethod method, int argument) {
+ // Ignored.
+ }
+
+ @Override
+ public void methodReturnsConstant(DexEncodedMethod method, long value) {
+ // Ignored.
+ }
+
+ @Override
+ public void methodNeverReturnsNull(DexEncodedMethod method) {
+ // Ignored.
+ }
+
+ @Override
+ public void methodNeverReturnsNormally(DexEncodedMethod method) {
+ // Ignored.
+ }
+
+ @Override
+ public void markProcessed(DexEncodedMethod method, Constraint state) {
+ // Just as processed, don't provide any inlining constraints.
+ method.markProcessed(Constraint.NEVER);
+ }
+
+ @Override
+ public void markCheckNullReceiverBeforeAnySideEffect(DexEncodedMethod method, boolean mark) {
+ // Ignored.
+ }
+
+ @Override
+ public void markTriggerClassInitBeforeAnySideEffect(DexEncodedMethod method, boolean mark) {
+ // Ignored.
+ }
+
+ @Override
+ public void setClassInlinerEligibility(
+ DexEncodedMethod method, ClassInlinerEligibility eligibility) {
+ // Ignored.
+ }
+
+ @Override
+ public void setTrivialInitializer(DexEncodedMethod method, TrivialInitializer info) {
+ // Ignored.
+ }
+
+ @Override
+ public void setInitializerEnablingJavaAssertions(DexEncodedMethod method) {
+ method.setInitializerEnablingJavaAssertions();
+ }
+
+ @Override
+ public void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo) {
+ // Ignored.
+ }
+
+ @Override
+ public void setKotlinNotNullParamHints(DexEncodedMethod method, BitSet hints) {
+ // Ignored.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 81407bd..86f3f17 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -29,11 +29,13 @@
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.google.common.base.Suppliers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
/**
* Represents lambda class generated for a lambda descriptor in context
@@ -64,6 +66,8 @@
final Target target;
final AtomicBoolean addToMainDexList = new AtomicBoolean(false);
private final Collection<DexProgramClass> synthesizedFrom = new ArrayList<DexProgramClass>(1);
+ private final Supplier<DexProgramClass> lazyDexClass =
+ Suppliers.memoize(this::synthesizeLambdaClass); // NOTE: thread-safe.
LambdaClass(LambdaRewriter rewriter, DexType accessedFrom,
DexType lambdaClassType, LambdaDescriptor descriptor) {
@@ -119,7 +123,11 @@
return rewriter.factory.createType(lambdaClassDescriptor.toString());
}
- final DexProgramClass synthesizeLambdaClass() {
+ final DexProgramClass getLambdaClass() {
+ return lazyDexClass.get();
+ }
+
+ private DexProgramClass synthesizeLambdaClass() {
return new DexProgramClass(
type,
null,
@@ -171,7 +179,7 @@
Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new SynthesizedCode(new LambdaMainMethodSourceCode(this, mainMethod)));
+ new SynthesizedCode(() -> new LambdaMainMethodSourceCode(this, mainMethod)));
// Synthesize bridge methods.
for (DexProto bridgeProto : descriptor.bridges) {
@@ -188,7 +196,7 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
new SynthesizedCode(
- new LambdaBridgeMethodSourceCode(this, mainMethod, bridgeMethod)));
+ () -> new LambdaBridgeMethodSourceCode(this, mainMethod, bridgeMethod)));
}
return methods;
}
@@ -208,7 +216,7 @@
true),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new SynthesizedCode(new LambdaConstructorSourceCode(this)));
+ new SynthesizedCode(() -> new LambdaConstructorSourceCode(this)));
// Class constructor for stateless lambda classes.
if (stateless) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index e7686db..51b8b8a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -141,7 +141,6 @@
if (method.name == deserializeLambdaMethodName &&
method.proto == deserializeLambdaMethodProto) {
assert encoded.accessFlags.isStatic();
- assert encoded.accessFlags.isPrivate();
assert encoded.accessFlags.isSynthetic();
DexEncodedMethod[] newMethods = new DexEncodedMethod[methodCount - 1];
@@ -170,10 +169,19 @@
}
}
+ /**
+ * Returns a synthetic class for desugared lambda or `null` if the `type`
+ * does not represent one. Method can be called concurrently.
+ */
+ public DexProgramClass getLambdaClass(DexType type) {
+ LambdaClass lambdaClass = getKnown(knownLambdaClasses, type);
+ return lambdaClass == null ? null : lambdaClass.getLambdaClass();
+ }
+
/** Generates lambda classes and adds them to the builder. */
public void synthesizeLambdaClasses(Builder<?> builder) {
for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
- DexProgramClass synthesizedClass = lambdaClass.synthesizeLambdaClass();
+ DexProgramClass synthesizedClass = lambdaClass.getLambdaClass();
converter.optimizeSynthesizedClass(synthesizedClass);
builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 94e8147..d87a508 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1265,7 +1265,29 @@
* }
* </pre>
*/
- public void disableAssertions(IRCode code) {
+ public void disableAssertions(
+ AppInfo appInfo, DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
+ if (method.isClassInitializer()) {
+ if (!hasJavacClinitAssertionCode(code)) {
+ return;
+ }
+ // Mark the clinit as having code to turn on assertions.
+ feedback.setInitializerEnablingJavaAssertions(method);
+ } else {
+ // If the clinit of this class did not have have code to turn on assertions don't try to
+ // remove assertion code from the method.
+ DexClass clazz = appInfo.definitionFor(method.method.holder);
+ if (clazz == null) {
+ return;
+ }
+ DexEncodedMethod clinit = clazz.getClassInitializer();
+ if (clinit == null
+ || !clinit.isProcessed()
+ || !clinit.getOptimizationInfo().isInitializerEnablingJavaAssertions()) {
+ return;
+ }
+ }
+
InstructionIterator iterator = code.instructionIterator();
while (iterator.hasNext()) {
Instruction current = iterator.next();
@@ -1288,6 +1310,78 @@
}
}
+ private boolean isClassDesiredAssertionStatusInvoke(Instruction instruction) {
+ return instruction.isInvokeMethod()
+ && instruction.asInvokeMethod().getInvokedMethod()
+ == dexItemFactory.classMethods.desiredAssertionStatus;
+ }
+
+ private boolean isAssertionsDisabledFieldPut(Instruction instruction) {
+ return instruction.isStaticPut()
+ && instruction.asStaticPut().getField().name == dexItemFactory.assertionsDisabled;
+ }
+
+ private boolean isNotDebugInstruction(Instruction instruction) {
+ return !instruction.isDebugInstruction();
+ }
+
+ private Value blockWithSingleConstNumberAndGoto(BasicBlock block) {
+ InstructionIterator iterator = block.iterator();
+ Instruction constNumber = iterator.nextUntil(this::isNotDebugInstruction);
+ if (constNumber == null || !constNumber.isConstNumber()) {
+ return null;
+ }
+ Instruction exit = iterator.nextUntil(this::isNotDebugInstruction);
+ return exit != null && exit.isGoto() ? constNumber.outValue() : null;
+ }
+
+ private Value blockWithAssertionsDisabledFieldPut(BasicBlock block) {
+ InstructionIterator iterator = block.iterator();
+ Instruction fieldPut = iterator.nextUntil(this::isNotDebugInstruction);
+ return fieldPut != null
+ && isAssertionsDisabledFieldPut(fieldPut) ? fieldPut.inValues().get(0) : null;
+ }
+
+ private boolean hasJavacClinitAssertionCode(IRCode code) {
+ InstructionIterator iterator = code.instructionIterator();
+ Instruction current = iterator.nextUntil(this::isClassDesiredAssertionStatusInvoke);
+ if (current == null) {
+ return false;
+ }
+
+ Value DesiredAssertionStatus = current.outValue();
+ assert iterator.hasNext();
+ current = iterator.next();
+ if (!current.isIf()
+ || !current.asIf().isZeroTest()
+ || current.asIf().inValues().get(0) != DesiredAssertionStatus) {
+ return false;
+ }
+
+ If theIf = current.asIf();
+ BasicBlock trueTarget = theIf.getTrueTarget();
+ BasicBlock falseTarget = theIf.fallthroughBlock();
+ if (trueTarget == falseTarget) {
+ return false;
+ }
+
+ Value trueValue = blockWithSingleConstNumberAndGoto(trueTarget);
+ Value falseValue = blockWithSingleConstNumberAndGoto(falseTarget);
+ if (trueValue == null
+ || falseValue == null
+ || (trueTarget.exit().asGoto().getTarget() != falseTarget.exit().asGoto().getTarget())) {
+ return false;
+ }
+
+ BasicBlock target = trueTarget.exit().asGoto().getTarget();
+ Value storeValue = blockWithAssertionsDisabledFieldPut(target);
+ return storeValue != null
+ && storeValue.isPhi()
+ && storeValue.asPhi().getOperands().size() == 2
+ && storeValue.asPhi().getOperands().contains(trueValue)
+ && storeValue.asPhi().getOperands().contains(falseValue);
+ }
+
// Check if the static put is a constant derived from the class holding the method.
// This checks for java.lang.Class.getName and java.lang.Class.getSimpleName.
private boolean isClassNameConstant(DexEncodedMethod method, StaticPut put) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index d3d1e2f..85f7a39 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -60,7 +60,8 @@
@Override
public void ensureMethodProcessed(DexEncodedMethod target, IRCode inlinee) {
- assert target.isProcessed();
+ // Do nothing. If the method is not yet processed, we still should
+ // be able to build IR for inlining, though.
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
new file mode 100644
index 0000000..9af3967
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
@@ -0,0 +1,147 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+// Per-class collection of method signatures.
+//
+// Example use case: to determine if a certain method can be publicized or not.
+public class MethodPoolCollection {
+
+ private static final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
+
+ private final DexApplication application;
+ private final Map<DexClass, MethodPool> methodPools = new ConcurrentHashMap<>();
+
+ public MethodPoolCollection(DexApplication application) {
+ this.application = application;
+ }
+
+ public void buildAll(ExecutorService executorService, Timing timing) throws ExecutionException {
+ timing.begin("Building method pool collection");
+ try {
+ List<Future<?>> futures = new ArrayList<>();
+ submitAll(application.classes(), futures, executorService);
+ ThreadUtils.awaitFutures(futures);
+ } finally {
+ timing.end();
+ }
+ }
+
+ public MethodPool get(DexClass clazz) {
+ assert methodPools.containsKey(clazz);
+ return methodPools.get(clazz);
+ }
+
+ public boolean markIfNotSeen(DexClass clazz, DexMethod method) {
+ MethodPool methodPool = get(clazz);
+ Wrapper<DexMethod> key = equivalence.wrap(method);
+ if (methodPool.hasSeen(key)) {
+ return true;
+ }
+ methodPool.seen(key);
+ return false;
+ }
+
+ private void submitAll(
+ Iterable<DexProgramClass> classes, List<Future<?>> futures, ExecutorService executorService) {
+ for (DexProgramClass clazz : classes) {
+ futures.add(executorService.submit(computeMethodPoolPerClass(clazz)));
+ }
+ }
+
+ private Runnable computeMethodPoolPerClass(DexClass clazz) {
+ return () -> {
+ MethodPool methodPool = methodPools.computeIfAbsent(clazz, k -> new MethodPool());
+ clazz.forEachMethod(
+ encodedMethod -> {
+ // We will add private instance methods when we promote them.
+ if (!encodedMethod.isPrivateMethod() || encodedMethod.isStaticMethod()) {
+ methodPool.seen(equivalence.wrap(encodedMethod.method));
+ }
+ });
+ if (clazz.superType != null) {
+ DexClass superClazz = application.definitionFor(clazz.superType);
+ if (superClazz != null) {
+ MethodPool superPool = methodPools.computeIfAbsent(superClazz, k -> new MethodPool());
+ superPool.linkSubtype(methodPool);
+ methodPool.linkSupertype(superPool);
+ }
+ }
+ if (clazz.isInterface()) {
+ clazz.type.forAllImplementsSubtypes(
+ implementer -> {
+ DexClass subClazz = application.definitionFor(implementer);
+ if (subClazz != null) {
+ MethodPool childPool = methodPools.computeIfAbsent(subClazz, k -> new MethodPool());
+ childPool.linkInterface(methodPool);
+ }
+ });
+ }
+ };
+ }
+
+ public static class MethodPool {
+ private MethodPool superType;
+ private final Set<MethodPool> interfaces = new HashSet<>();
+ private final Set<MethodPool> subTypes = new HashSet<>();
+ private final Set<Wrapper<DexMethod>> methodPool = new HashSet<>();
+
+ private MethodPool() {}
+
+ synchronized void linkSupertype(MethodPool superType) {
+ assert this.superType == null;
+ this.superType = superType;
+ }
+
+ synchronized void linkSubtype(MethodPool subType) {
+ boolean added = subTypes.add(subType);
+ assert added;
+ }
+
+ synchronized void linkInterface(MethodPool itf) {
+ boolean added = interfaces.add(itf);
+ assert added;
+ }
+
+ public synchronized void seen(Wrapper<DexMethod> method) {
+ boolean added = methodPool.add(method);
+ assert added;
+ }
+
+ public boolean hasSeen(Wrapper<DexMethod> method) {
+ return hasSeenUpwardRecursive(method) || hasSeenDownwardRecursive(method);
+ }
+
+ private boolean hasSeenUpwardRecursive(Wrapper<DexMethod> method) {
+ return methodPool.contains(method)
+ || (superType != null && superType.hasSeenUpwardRecursive(method))
+ || interfaces.stream().anyMatch(itf -> itf.hasSeenUpwardRecursive(method));
+ }
+
+ private boolean hasSeenDownwardRecursive(Wrapper<DexMethod> method) {
+ return methodPool.contains(method)
+ || subTypes.stream().anyMatch(subType -> subType.hasSeenDownwardRecursive(method));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index dee935e..9b0ab04 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -112,6 +112,11 @@
assert !couldBeVolatile(field);
if (instruction.isInstanceGet() && !instruction.outValue().hasLocalInfo()) {
Value object = instruction.asInstanceGet().object();
+ // Values from NonNull instructions will always be replaced with their original
+ // value before code is generated.
+ if (!object.isPhi() && object.definition.isNonNull()) {
+ object = object.definition.asNonNull().src();
+ }
FieldAndObject fieldAndObject = new FieldAndObject(field, object);
if (activeInstanceFields.containsKey(fieldAndObject)) {
Instruction active = activeInstanceFields.get(fieldAndObject);
@@ -132,7 +137,8 @@
}
}
}
- if (instruction.isMonitor() || instruction.isInvokeMethod()) {
+ if ((instruction.isMonitor() && instruction.asMonitor().isEnter())
+ || instruction.isInvokeMethod()) {
killAllActiveFields();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 5bf2c74..3a2371d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -8,10 +8,10 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
import com.android.tools.r8.ir.optimize.InliningOracle;
@@ -27,15 +27,18 @@
public final class ClassInliner {
private final DexItemFactory factory;
+ private final LambdaRewriter lambdaRewriter;
private final int totalMethodInstructionLimit;
- private final ConcurrentHashMap<DexType, Boolean> knownClasses = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<DexClass, Boolean> knownClasses = new ConcurrentHashMap<>();
public interface InlinerAction {
void inline(Map<InvokeMethod, InliningInfo> methods);
}
- public ClassInliner(DexItemFactory factory, int totalMethodInstructionLimit) {
+ public ClassInliner(DexItemFactory factory,
+ LambdaRewriter lambdaRewriter, int totalMethodInstructionLimit) {
this.factory = factory;
+ this.lambdaRewriter = lambdaRewriter;
this.totalMethodInstructionLimit = totalMethodInstructionLimit;
}
@@ -142,8 +145,8 @@
while (rootsIterator.hasNext()) {
Instruction root = rootsIterator.next();
InlineCandidateProcessor processor =
- new InlineCandidateProcessor(factory, appInfo,
- type -> isClassEligible(appInfo, type),
+ new InlineCandidateProcessor(factory, appInfo, lambdaRewriter,
+ clazz -> isClassEligible(appInfo, clazz),
isProcessedConcurrently, method, root);
// Assess eligibility of instance and class.
@@ -180,7 +183,7 @@
} while (repeat);
}
- private boolean isClassEligible(AppInfo appInfo, DexType clazz) {
+ private boolean isClassEligible(AppInfo appInfo, DexClass clazz) {
Boolean eligible = knownClasses.get(clazz);
if (eligible == null) {
Boolean computed = computeClassEligible(appInfo, clazz);
@@ -195,15 +198,14 @@
// - is not an abstract class or interface
// - does not declare finalizer
// - does not trigger any static initializers except for its own
- private boolean computeClassEligible(AppInfo appInfo, DexType clazz) {
- DexClass definition = appInfo.definitionFor(clazz);
- if (definition == null || definition.isLibraryClass() ||
- definition.accessFlags.isAbstract() || definition.accessFlags.isInterface()) {
+ private boolean computeClassEligible(AppInfo appInfo, DexClass clazz) {
+ if (clazz == null || clazz.isLibraryClass() ||
+ clazz.accessFlags.isAbstract() || clazz.accessFlags.isInterface()) {
return false;
}
// Class must not define finalizer.
- for (DexEncodedMethod method : definition.virtualMethods()) {
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
if (method.method.name == factory.finalizeMethodName &&
method.method.proto == factory.objectMethods.finalize.proto) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 57ce42f..5fedf37 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -53,7 +54,8 @@
private final DexItemFactory factory;
private final AppInfoWithLiveness appInfo;
- private final Predicate<DexType> isClassEligible;
+ private final LambdaRewriter lambdaRewriter;
+ private final Predicate<DexClass> isClassEligible;
private final Predicate<DexEncodedMethod> isProcessedConcurrently;
private final DexEncodedMethod method;
private final Instruction root;
@@ -61,6 +63,7 @@
private Value eligibleInstance;
private DexType eligibleClass;
private DexClass eligibleClassDefinition;
+ private boolean isDesugaredLambda;
private final Map<InvokeMethod, InliningInfo> methodCallsOnInstance
= new IdentityHashMap<>();
@@ -73,10 +76,11 @@
InlineCandidateProcessor(
DexItemFactory factory, AppInfoWithLiveness appInfo,
- Predicate<DexType> isClassEligible,
+ LambdaRewriter lambdaRewriter, Predicate<DexClass> isClassEligible,
Predicate<DexEncodedMethod> isProcessedConcurrently,
DexEncodedMethod method, Instruction root) {
this.factory = factory;
+ this.lambdaRewriter = lambdaRewriter;
this.isClassEligible = isClassEligible;
this.method = method;
this.root = root;
@@ -99,6 +103,11 @@
eligibleClass = isNewInstance() ?
root.asNewInstance().clazz : root.asStaticGet().getField().type;
eligibleClassDefinition = appInfo.definitionFor(eligibleClass);
+ if (eligibleClassDefinition == null && lambdaRewriter != null) {
+ // Check if the class is synthesized for a desugared lambda
+ eligibleClassDefinition = lambdaRewriter.getLambdaClass(eligibleClass);
+ isDesugaredLambda = eligibleClassDefinition != null;
+ }
return eligibleClassDefinition != null;
}
@@ -114,7 +123,7 @@
// * class has class initializer marked as TrivialClassInitializer, and
// class initializer initializes the field we are reading here.
boolean isClassAndUsageEligible() {
- if (!isClassEligible.test(eligibleClass)) {
+ if (!isClassEligible.test(eligibleClassDefinition)) {
return false;
}
@@ -129,6 +138,11 @@
assert root.isStaticGet();
+ // We know that desugared lambda classes satisfy eligibility requirements.
+ if (isDesugaredLambda) {
+ return true;
+ }
+
// Checking if we can safely inline class implemented following singleton-like
// pattern, by which we assume a static final field holding on to the reference
// initialized in class constructor.
@@ -444,7 +458,7 @@
: "Inlined constructor? [invoke: " + initInvoke +
", expected class: " + eligibleClass + "]";
- DexEncodedMethod definition = appInfo.definitionFor(init);
+ DexEncodedMethod definition = findSingleTarget(init, true);
if (definition == null || isProcessedConcurrently.test(definition)) {
return null;
}
@@ -455,6 +469,12 @@
return null;
}
+ if (isDesugaredLambda) {
+ // Lambda desugaring synthesizes eligible constructors.
+ markSizeForInlining(definition);
+ return new InliningInfo(definition, eligibleClass);
+ }
+
// If the superclass of the initializer is NOT java.lang.Object, the super class
// initializer being called must be classified as TrivialInstanceInitializer.
//
@@ -499,7 +519,7 @@
private InliningInfo isEligibleMethodCall(boolean allowMethodsWithoutNormalReturns,
DexMethod callee, Predicate<ClassInlinerEligibility> eligibilityAcceptanceCheck) {
- DexEncodedMethod singleTarget = findSingleTarget(callee);
+ DexEncodedMethod singleTarget = findSingleTarget(callee, false);
if (singleTarget == null || isProcessedConcurrently.test(singleTarget)) {
return null;
}
@@ -507,6 +527,16 @@
return null; // Don't inline itself.
}
+ if (isDesugaredLambda) {
+ // If this is the call to method of the desugared lambda, we consider only calls
+ // to main lambda method eligible (for both direct and indirect calls).
+ if (singleTarget.accessFlags.isBridge()) {
+ return null;
+ }
+ markSizeForInlining(singleTarget);
+ return new InliningInfo(singleTarget, eligibleClass);
+ }
+
OptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
ClassInlinerEligibility eligibility = optimizationInfo.getClassInlinerEligibility();
@@ -652,6 +682,9 @@
private boolean exemptFromInstructionLimit(DexEncodedMethod inlinee) {
DexType inlineeHolder = inlinee.method.holder;
+ if (isDesugaredLambda && inlineeHolder == eligibleClass) {
+ return true;
+ }
if (appInfo.isPinned(inlineeHolder)) {
return false;
}
@@ -674,14 +707,16 @@
return root.isNewInstance();
}
- private DexEncodedMethod findSingleTarget(DexMethod callee) {
+ private DexEncodedMethod findSingleTarget(DexMethod callee, boolean isDirect) {
// We don't use computeSingleTarget(...) on invoke since it sometimes fails to
// find the single target, while this code may be more successful since we exactly
// know what is the actual type of the receiver.
// Note that we also intentionally limit ourselves to methods directly defined in
// the instance's class. This may be improved later.
- return eligibleClassDefinition.lookupVirtualMethod(callee);
+ return isDirect
+ ? eligibleClassDefinition.lookupDirectMethod(callee)
+ : eligibleClassDefinition.lookupVirtualMethod(callee);
}
private void removeInstruction(Instruction instruction) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
index a5807c3..7835da2 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
@@ -10,25 +10,33 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.SourceCode;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
import java.util.function.Consumer;
+import java.util.function.Supplier;
public final class SynthesizedCode extends Code {
- private final SourceCode sourceCode;
+ private final Supplier<SourceCode> sourceCodeProvider;
private final Consumer<UseRegistry> registryCallback;
- public SynthesizedCode(SourceCode sourceCode) {
- this.sourceCode = sourceCode;
+ public SynthesizedCode(SourceCode sourceCodeProvider) {
+ this(() -> sourceCodeProvider);
+ }
+
+ public SynthesizedCode(Supplier<SourceCode> sourceCodeProvider) {
+ this.sourceCodeProvider = sourceCodeProvider;
this.registryCallback = SynthesizedCode::registerReachableDefinitionsDefault;
}
- public SynthesizedCode(SourceCode sourceCode, Consumer<UseRegistry> callback) {
- this.sourceCode = sourceCode;
+ public SynthesizedCode(
+ SourceCode sourceCode, Consumer<UseRegistry> callback) {
+ this.sourceCodeProvider = () -> sourceCode;
this.registryCallback = callback;
}
@@ -40,7 +48,15 @@
@Override
public final IRCode buildIR(
DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
- return new IRBuilder(encodedMethod, appInfo, sourceCode, options).build();
+ return new IRBuilder(encodedMethod, appInfo, sourceCodeProvider.get(), options).build();
+ }
+
+ @Override
+ public IRCode buildInliningIR(
+ DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options,
+ ValueNumberGenerator valueNumberGenerator, Position callerPosition, Origin origin) {
+ return new IRBuilder(encodedMethod, appInfo,
+ sourceCodeProvider.get(), options, valueNumberGenerator).build();
}
@Override
@@ -59,17 +75,16 @@
@Override
protected final int computeHashCode() {
- return sourceCode.hashCode();
+ throw new Unreachable();
}
@Override
protected final boolean computeEquals(Object other) {
- return other instanceof SynthesizedCode &&
- this.sourceCode.equals(((SynthesizedCode) other).sourceCode);
+ throw new Unreachable();
}
@Override
public final String toString(DexEncodedMethod method, ClassNameMapper naming) {
- return "SynthesizedCode: " + sourceCode.toString();
+ return "SynthesizedCode";
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 05c7b92..fb6ef33 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -10,11 +10,20 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.Set;
/** Class provides basic information about symbols related to Kotlin support. */
public final class Kotlin {
+ // Simply "Lkotlin/", but to avoid being renamed by Shadow.relocate
+ private static final String KOTLIN =
+ String.join("", ImmutableList.of("L", "k", "o", "t", "l", "i", "n", "/"));
+
+ static String addKotlinPrefix(String str) {
+ return KOTLIN + str;
+ }
+
public final DexItemFactory factory;
public final Functional functional;
@@ -44,14 +53,15 @@
//
// This implementation just ignores lambdas with arity > 22.
for (int i = 0; i <= 22; i++) {
- functions.add(factory.createType("Lkotlin/jvm/functions/Function" + i + ";"));
+ functions.add(factory.createType(addKotlinPrefix("jvm/functions/Function") + i + ";"));
}
}
public final DexString kotlinStyleLambdaInstanceName = factory.createString("INSTANCE");
- public final DexType functionBase = factory.createType("Lkotlin/jvm/internal/FunctionBase;");
- public final DexType lambdaType = factory.createType("Lkotlin/jvm/internal/Lambda;");
+ public final DexType functionBase =
+ factory.createType(addKotlinPrefix("jvm/internal/FunctionBase;"));
+ public final DexType lambdaType = factory.createType(addKotlinPrefix("jvm/internal/Lambda;"));
public final DexMethod lambdaInitializerMethod = factory.createMethod(
lambdaType,
@@ -64,7 +74,7 @@
}
public final class Metadata {
- public final DexType kotlinMetadataType = factory.createType("Lkotlin/Metadata;");
+ public final DexType kotlinMetadataType = factory.createType(addKotlinPrefix("Metadata;"));
public final DexString kind = factory.createString("k");
public final DexString metadataVersion = factory.createString("mv");
public final DexString bytecodeVersion = factory.createString("bv");
@@ -77,7 +87,7 @@
// kotlin.jvm.internal.Intrinsics class
public final class Intrinsics {
- public final DexType type = factory.createType("Lkotlin/jvm/internal/Intrinsics;");
+ public final DexType type = factory.createType(addKotlinPrefix("jvm/internal/Intrinsics;"));
public final DexMethod throwParameterIsNullException = factory.createMethod(type,
factory.createProto(factory.voidType, factory.stringType), "throwParameterIsNullException");
public final DexMethod checkParameterIsNotNull = factory.createMethod(type,
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index 32aebff..389a8f8 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -36,7 +36,7 @@
new StringDiagnostic("Class " + clazz.type.toSourceString()
+ " has malformed kotlin.Metadata: " + e.getMessage()));
} catch (Throwable e) {
- reporter.warning(
+ reporter.warning(
new StringDiagnostic("Unexpected error while reading " + clazz.type.toSourceString()
+ "'s kotlin.Metadata: " + e.getMessage()));
}
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 73ebf73..50571f3 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -11,23 +11,16 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.optimize.MethodPoolCollection;
import com.android.tools.r8.optimize.PublicizerLense.PublicizedLenseBuilder;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
-import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.base.Equivalence;
-import com.google.common.base.Equivalence.Wrapper;
-import java.util.ArrayList;
-import java.util.HashSet;
import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
public final class ClassAndMemberPublicizer {
private final DexApplication application;
@@ -36,11 +29,12 @@
private final PublicizedLenseBuilder lenseBuilder;
private final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
- private final Map<DexClass, MethodPool> methodPools = new ConcurrentHashMap<>();
+ private final MethodPoolCollection methodPoolCollection;
private ClassAndMemberPublicizer(DexApplication application, AppView appView, RootSet rootSet) {
this.application = application;
this.appView = appView;
+ this.methodPoolCollection = new MethodPoolCollection(application);
this.rootSet = rootSet;
lenseBuilder = PublicizerLense.createBuilder();
}
@@ -64,15 +58,7 @@
private GraphLense run(ExecutorService executorService, Timing timing)
throws ExecutionException {
// Phase 1: Collect methods to check if private instance methods don't have conflicts.
- timing.begin("Phase 1: collectMethods");
- try {
- List<Future<?>> futures = new ArrayList<>();
- application.classes().forEach(clazz ->
- futures.add(executorService.submit(computeMethodPoolPerClass(clazz))));
- ThreadUtils.awaitFutures(futures);
- } finally {
- timing.end();
- }
+ methodPoolCollection.buildAll(executorService, timing);
// Phase 2: Visit classes and promote class/member to public if possible.
timing.begin("Phase 2: promoteToPublic");
@@ -83,35 +69,6 @@
return lenseBuilder.build(appView);
}
- private Runnable computeMethodPoolPerClass(DexClass clazz) {
- return () -> {
- MethodPool methodPool = methodPools.computeIfAbsent(clazz, k -> new MethodPool());
- clazz.forEachMethod(encodedMethod -> {
- // We will add private instance methods when we promote them.
- if (!encodedMethod.isPrivateMethod() || encodedMethod.isStaticMethod()) {
- methodPool.seen(equivalence.wrap(encodedMethod.method));
- }
- });
- if (clazz.superType != null) {
- DexClass superClazz = application.definitionFor(clazz.superType);
- if (superClazz != null) {
- MethodPool superPool = methodPools.computeIfAbsent(superClazz, k -> new MethodPool());
- superPool.linkSubtype(methodPool);
- methodPool.linkSupertype(superPool);
- }
- }
- if (clazz.isInterface()) {
- clazz.type.forAllImplementsSubtypes(implementer -> {
- DexClass subClazz = application.definitionFor(implementer);
- if (subClazz != null) {
- MethodPool childPool = methodPools.computeIfAbsent(subClazz, k -> new MethodPool());
- childPool.linkInterface(methodPool);
- }
- });
- }
- };
- }
-
private void publicizeType(DexType type) {
DexClass clazz = application.definitionFor(type);
if (clazz != null && clazz.isProgramClass()) {
@@ -149,7 +106,8 @@
assert accessFlags.isPrivate();
if (appView.getDexItemFactory().isConstructor(encodedMethod.method)) {
- // TODO(b/72211928)
+ accessFlags.unsetPrivate();
+ accessFlags.setPublic();
return false;
}
@@ -166,9 +124,8 @@
return false;
}
- MethodPool methodPool = methodPools.get(holder);
- Wrapper<DexMethod> key = equivalence.wrap(encodedMethod.method);
- if (methodPool.hasSeen(key)) {
+ boolean wasSeen = methodPoolCollection.markIfNotSeen(holder, encodedMethod.method);
+ if (wasSeen) {
// We can't do anything further because even renaming is not allowed due to the keep rule.
if (rootSet.noObfuscation.contains(encodedMethod)) {
return false;
@@ -176,7 +133,6 @@
// TODO(b/111118390): Renaming will enable more private instance methods to be publicized.
return false;
}
- methodPool.seen(key);
lenseBuilder.add(encodedMethod.method);
accessFlags.unsetPrivate();
accessFlags.setFinal();
@@ -196,51 +152,4 @@
accessFlags.setPublic();
return false;
}
-
- // Per-class collection of method signatures, which will be used to determine if a certain method
- // can be publicized or not.
- static class MethodPool {
- private MethodPool superType;
- private final Set<MethodPool> interfaces = new HashSet<>();
- private final Set<MethodPool> subTypes = new HashSet<>();
- private final Set<Wrapper<DexMethod>> methodPool = new HashSet<>();
-
- MethodPool() {
- }
-
- synchronized void linkSupertype(MethodPool superType) {
- assert this.superType == null;
- this.superType = superType;
- }
-
- synchronized void linkSubtype(MethodPool subType) {
- boolean added = subTypes.add(subType);
- assert added;
- }
-
- synchronized void linkInterface(MethodPool itf) {
- boolean added = interfaces.add(itf);
- assert added;
- }
-
- synchronized void seen(Wrapper<DexMethod> method) {
- boolean added = methodPool.add(method);
- assert added;
- }
-
- boolean hasSeen(Wrapper<DexMethod> method) {
- return hasSeenUpwardRecursive(method) || hasSeenDownwardRecursive(method);
- }
-
- private boolean hasSeenUpwardRecursive(Wrapper<DexMethod> method) {
- return methodPool.contains(method)
- || (superType != null && superType.hasSeenUpwardRecursive(method))
- || interfaces.stream().anyMatch(itf -> itf.hasSeenUpwardRecursive(method));
- }
-
- private boolean hasSeenDownwardRecursive(Wrapper<DexMethod> method) {
- return methodPool.contains(method)
- || subTypes.stream().anyMatch(subType -> subType.hasSeenDownwardRecursive(method));
- }
- }
}
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 eb8ee0e..93a80b6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -447,7 +447,9 @@
public boolean placeExceptionalBlocksLast = false;
public boolean dontCreateMarkerInD8 = false;
public boolean forceJumboStringProcessing = false;
+ public boolean nondeterministicCycleElimination = false;
public Set<Inliner.Reason> validInliningReasons = null;
+ public boolean suppressExperimentalCfBackendWarning = false;
}
public boolean canUseInvokePolymorphicOnVarHandle() {
diff --git a/src/test/examples/classmerging/CallGraphCycleTest.java b/src/test/examples/classmerging/CallGraphCycleTest.java
new file mode 100644
index 0000000..40347d6
--- /dev/null
+++ b/src/test/examples/classmerging/CallGraphCycleTest.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package classmerging;
+
+public class CallGraphCycleTest {
+
+ public static void main(String[] args) {
+ new B(true);
+ }
+
+ public static class A {
+
+ public A(boolean instantiateB) {
+ if (instantiateB) {
+ new B(false);
+ }
+ System.out.println("A(" + instantiateB + ")");
+ }
+ }
+
+ public static class B extends A {
+
+ public B(boolean instantiateBinA) {
+ super(instantiateBinA);
+ System.out.println("B(" + instantiateBinA + ")");
+ }
+ }
+}
diff --git a/src/test/examples/classmerging/keep-rules.txt b/src/test/examples/classmerging/keep-rules.txt
index 4ba014e..37a3010 100644
--- a/src/test/examples/classmerging/keep-rules.txt
+++ b/src/test/examples/classmerging/keep-rules.txt
@@ -7,6 +7,9 @@
-keep public class classmerging.Test {
public static void main(...);
}
+-keep public class classmerging.CallGraphCycleTest {
+ public static void main(...);
+}
-keep public class classmerging.ClassWithNativeMethodTest {
public static void main(...);
}
diff --git a/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java
index b3950b4..c773989 100644
--- a/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 7328c6e..801abae 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -12,18 +12,17 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ArtErrorParser;
import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.TestDescriptionWatcher;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index f20335b..36a5fb3 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -4,12 +4,16 @@
package com.android.tools.r8;
+import static org.junit.Assert.assertEquals;
+
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderThan;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.OffOrAuto;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FoundClassSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
@@ -94,6 +98,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 179, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
@@ -101,6 +106,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = true)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 23, "lambdadesugaring"))
.run();
}
@@ -112,6 +118,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 179, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
@@ -119,6 +126,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = true)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 23, "lambdadesugaring"))
.run();
}
@@ -131,6 +139,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 40, "lambdadesugaringnplus"))
.run();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
@@ -139,6 +148,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = true)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 5, "lambdadesugaringnplus"))
.run();
}
@@ -151,6 +161,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 40, "lambdadesugaringnplus"))
.run();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
@@ -159,9 +170,21 @@
.withOptionConsumer(opts -> opts.enableClassInlining = true)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 5, "lambdadesugaringnplus"))
.run();
}
+ private void checkLambdaCount(DexInspector inspector, int expectedCount, String prefix) {
+ int count = 0;
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ if (clazz.isSynthesizedJavaLambdaClass() &&
+ clazz.getOriginalName().startsWith(prefix)) {
+ count++;
+ }
+ }
+ assertEquals(expectedCount, count);
+ }
+
class R8TestRunner extends TestRunner<R8TestRunner> {
R8TestRunner(String testName, String packageName, String mainClass) {
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index b0876c9..2a17917 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -14,14 +14,14 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
-import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OffOrAuto;
import com.android.tools.r8.utils.TestDescriptionWatcher;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FoundClassSubject;
+import com.android.tools.r8.utils.dexinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
+import com.android.tools.r8.utils.dexinspector.InvokeInstructionSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
@@ -49,7 +49,6 @@
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
-import org.junit.rules.TestWatcher;
public abstract class RunExamplesAndroidOTest
<B extends BaseCommand.Builder<? extends BaseCommand, B>> {
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index 4701ac3..aa17280 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -13,13 +13,13 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
-import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OffOrAuto;
import com.android.tools.r8.utils.TestDescriptionWatcher;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FoundClassSubject;
+import com.android.tools.r8.utils.dexinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
diff --git a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
index 60ebf39..d1894fb 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
@@ -4,9 +4,9 @@
package com.android.tools.r8;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -14,15 +14,15 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
-import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OffOrAuto;
import com.android.tools.r8.utils.TestDescriptionWatcher;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FoundClassSubject;
+import com.android.tools.r8.utils.dexinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 8adddfd..2d951fa 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -20,14 +20,14 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.PreloadedClassFileProvider;
import com.android.tools.r8.utils.TestDescriptionWatcher;
import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -54,6 +54,11 @@
public class TestBase {
+ protected enum Backend {
+ CF,
+ DEX
+ };
+
// Actually running Proguard should only be during development.
private static final boolean RUN_PROGUARD = System.getProperty("run_proguard") != null;
// Actually running r8.jar in a forked process.
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2667ce2..2767df7 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -474,8 +474,9 @@
public static byte[] getClassAsBytes(Class clazz) throws IOException {
String s = clazz.getSimpleName() + ".class";
Class outer = clazz.getEnclosingClass();
- if (outer != null) {
+ while (outer != null) {
s = outer.getSimpleName() + '$' + s;
+ outer = outer.getEnclosingClass();
}
return ByteStreams.toByteArray(clazz.getResourceAsStream(s));
}
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
new file mode 100644
index 0000000..706b50b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.accessrelaxation;
+
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPublic;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
+import org.junit.runner.RunWith;
+
+@RunWith(VmTestRunner.class)
+abstract class AccessRelaxationTestBase extends TestBase {
+
+ static R8Command.Builder loadProgramFiles(Iterable<Class> classes) {
+ R8Command.Builder builder = R8Command.builder();
+ for (Class clazz : classes) {
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
+ }
+ builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+ return builder;
+ }
+
+ static R8Command.Builder loadProgramFiles(Package p, Class... classes) throws Exception {
+ R8Command.Builder builder = R8Command.builder();
+ builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(p));
+ for (Class clazz : classes) {
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
+ }
+ builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+ return builder;
+ }
+
+ void compareJvmAndArt(AndroidApp app, Class mainClass) throws Exception {
+ // Run on Jvm.
+ String jvmOutput = runOnJava(mainClass);
+
+ // Run on Art to check generated code against verifier.
+ String artOutput = runOnArt(app, mainClass);
+
+ String adjustedArtOutput = artOutput.replace(
+ "java.lang.IncompatibleClassChangeError", "java.lang.IllegalAccessError");
+ assertEquals(jvmOutput, adjustedArtOutput);
+ }
+
+ static void assertPublic(DexInspector dexInspector, Class clazz, MethodSignature signature) {
+ ClassSubject classSubject = dexInspector.clazz(clazz);
+ assertThat(classSubject, isPresent());
+ MethodSubject methodSubject = classSubject.method(signature);
+ assertThat(methodSubject, isPublic());
+ }
+
+ static void assertNotPublic(DexInspector dexInspector, Class clazz, MethodSignature signature) {
+ ClassSubject classSubject = dexInspector.clazz(clazz);
+ assertThat(classSubject, isPresent());
+ MethodSubject methodSubject = classSubject.method(signature);
+ assertThat(methodSubject, not(isPublic()));
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
new file mode 100644
index 0000000..713c467
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
@@ -0,0 +1,198 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.accessrelaxation;
+
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import org.junit.Test;
+
+class L1 {
+ private final String x;
+
+ private L1(String x) {
+ this.x = x;
+ }
+
+ private L1() {
+ this("private_x");
+ }
+
+ static L1 create() {
+ return new L1();
+ }
+
+ L1(int i) {
+ this(String.valueOf(i));
+ }
+
+ @Override
+ public String toString() {
+ return x;
+ }
+}
+
+class L2_1 extends L1 {
+ private String y;
+
+ private L2_1() {
+ this(21);
+ this.y = "private_L2_1_y";
+ }
+
+ L2_1(int i) {
+ super(i);
+ this.y = "L2_1_y";
+ }
+
+ private L2_1(String y) {
+ this(21);
+ this.y = y;
+ }
+
+ static L2_1 create(String y) {
+ return new L2_1(y);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "_" + y;
+ }
+}
+
+class L2_2 extends L1 {
+ private String y;
+
+ private L2_2(int i) {
+ super(i);
+ this.y = "private_L2_2_y";
+ }
+
+ L2_2(String y) {
+ this(22);
+ this.y = y;
+ }
+
+ static L2_1 create() {
+ return new L2_1(22);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "_" + y;
+ }
+}
+
+class L3_1 extends L2_1 {
+ private final String z;
+
+ private L3_1(int i) {
+ this(String.valueOf(i));
+ }
+
+ private L3_1(String z) {
+ super(31);
+ this.z = z;
+ }
+
+ static L3_1 create(int i) {
+ return new L3_1(i);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "_" + z;
+ }
+}
+
+class L3_2 extends L2_2 {
+ private String z;
+
+ private L3_2() {
+ super("private_L3_2_y");
+ this.z = "private_L3_2_z";
+ }
+
+ private L3_2(int i) {
+ super(String.valueOf(i));
+ this.z = "private_L3_2_z" + "_" + i;
+ }
+
+ L3_2(String z) {
+ this(32);
+ this.z = z;
+ }
+
+ static L3_2 create(String z) {
+ return new L3_2(z);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "_" + z;
+ }
+}
+
+class CtorTestMain {
+ public static void main(String[] args) {
+ System.out.println(L1.create());
+ System.out.println(L2_1.create("main_y"));
+ System.out.println(L2_2.create());
+ System.out.println(L3_1.create(41));
+ System.out.println(L3_2.create("main_z"));
+ }
+}
+
+public final class ConstructorRelaxationTest extends AccessRelaxationTestBase {
+ private static final String INIT= "<init>";
+ private static final List<Class> CLASSES =
+ ImmutableList.of(L1.class, L2_1.class, L2_2.class, L3_1.class, L3_2.class);
+
+ @Test
+ public void test() throws Exception {
+ Class mainClass = CtorTestMain.class;
+ R8Command.Builder builder =
+ loadProgramFiles(Iterables.concat(CLASSES, ImmutableList.of(mainClass)));
+ builder.addProguardConfiguration(
+ ImmutableList.of(
+ "-keep class " + mainClass.getCanonicalName() + "{",
+ " public static void main(java.lang.String[]);",
+ "}",
+ "",
+ "-keep class *.L* {",
+ " <init>(...);",
+ "}",
+ "",
+ "-dontobfuscate",
+ "-allowaccessmodification"
+ ),
+ Origin.unknown());
+
+ AndroidApp app = ToolHelper.runR8(builder.build(), options -> {
+ options.enableInlining = false;
+ options.enableClassMerging = false;
+ });
+ compareJvmAndArt(app, mainClass);
+
+ DexInspector dexInspector = new DexInspector(app);
+ for (Class clazz : CLASSES) {
+ ClassSubject classSubject = dexInspector.clazz(clazz);
+ assertThat(classSubject, isPresent());
+ classSubject.getDexClass().forEachMethod(m -> {
+ assertTrue(!m.isInstanceInitializer() || m.isPublicMethod());
+ });
+ }
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java
index 1f97c11..4459699 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.accessrelaxation;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -24,8 +24,8 @@
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.function.Consumer;
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
similarity index 69%
rename from src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTest.java
rename to src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
index 53a8c31..9bfd4e2 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
@@ -4,17 +4,9 @@
package com.android.tools.r8.accessrelaxation;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPublic;
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.R8Command;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.VmTestRunner;
import com.android.tools.r8.accessrelaxation.privateinstance.Base;
import com.android.tools.r8.accessrelaxation.privateinstance.Sub1;
import com.android.tools.r8.accessrelaxation.privateinstance.Sub2;
@@ -26,56 +18,13 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
-import org.junit.runner.RunWith;
-@RunWith(VmTestRunner.class)
-public class AccessRelaxationTest extends TestBase {
+public final class NonConstructorRelaxationTest extends AccessRelaxationTestBase {
private static final String STRING = "java.lang.String";
- private static R8Command.Builder loadProgramFiles(Package p, Class... classes) throws Exception {
- R8Command.Builder builder = R8Command.builder();
- builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(p));
- for (Class clazz : classes) {
- builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
- }
- builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
- builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
- return builder;
- }
-
- private void compareJvmAndArt(AndroidApp app, Class mainClass) throws Exception {
- // Run on Jvm.
- String jvmOutput = runOnJava(mainClass);
-
- // Run on Art to check generated code against verifier.
- String artOutput = runOnArt(app, mainClass);
-
- String adjustedArtOutput = artOutput.replace(
- "java.lang.IncompatibleClassChangeError", "java.lang.IllegalAccessError");
- assertEquals(jvmOutput, adjustedArtOutput);
- }
-
- private static void assertPublic(
- DexInspector dexInspector, Class clazz, MethodSignature signature) {
- ClassSubject classSubject = dexInspector.clazz(clazz);
- assertThat(classSubject, isPresent());
- MethodSubject methodSubject = classSubject.method(signature);
- assertThat(methodSubject, isPublic());
- }
-
- private static void assertNotPublic(
- DexInspector dexInspector, Class clazz, MethodSignature signature) {
- ClassSubject classSubject = dexInspector.clazz(clazz);
- assertThat(classSubject, isPresent());
- MethodSubject methodSubject = classSubject.method(signature);
- assertThat(methodSubject, not(isPublic()));
- }
-
@Test
public void testStaticMethodRelaxation() throws Exception {
Class mainClass = C.class;
diff --git a/src/test/java/com/android/tools/r8/bisect/BisectTest.java b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
index 82a4a94..a692b9e 100644
--- a/src/test/java/com/android/tools/r8/bisect/BisectTest.java
+++ b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
@@ -14,9 +14,9 @@
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index cc8633e4..77b251d 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.bridgeremoval;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -16,9 +16,9 @@
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/EmptyBridgeTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/EmptyBridgeTest.java
index 160ad59..8fcf7a3 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/EmptyBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/EmptyBridgeTest.java
@@ -3,16 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.bridgeremoval;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertThat;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
index 60df7d2..272406f 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.bridgeremoval;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
@@ -17,9 +17,9 @@
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
index 55ff3a2..f1eddcd 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
@@ -7,8 +7,8 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java b/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
index fd696bc..d285dc6 100644
--- a/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
@@ -19,7 +19,7 @@
import com.android.tools.r8.graph.DexCode.Try;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
diff --git a/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java b/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java
index 6c9d7b6..e1d110c 100644
--- a/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java
@@ -18,7 +18,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
diff --git a/src/test/java/com/android/tools/r8/cf/SynchronizedNoopTestRunner.java b/src/test/java/com/android/tools/r8/cf/SynchronizedNoopTestRunner.java
index 7565dee..6196081 100644
--- a/src/test/java/com/android/tools/r8/cf/SynchronizedNoopTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/SynchronizedNoopTestRunner.java
@@ -15,7 +15,7 @@
import com.android.tools.r8.graph.JarCode;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidAppConsumers;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.util.ArrayList;
import java.util.Collections;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index b6489b0..91e7b5d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.classmerging;
import static com.android.tools.r8.smali.SmaliBuilder.buildCode;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -29,11 +29,11 @@
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FoundClassSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -70,6 +70,7 @@
options.enableClassMerging = true;
options.enableClassInlining = false;
options.enableMinification = false;
+ options.testing.nondeterministicCycleElimination = true;
}
private void runR8(Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
@@ -115,6 +116,29 @@
}
}
+ // This test has a cycle in the call graph consisting of the methods A.<init> and B.<init>.
+ // When nondeterministicCycleElimination is enabled, we shuffle the nodes in the call graph
+ // before the cycle elimination. Therefore, it is nondeterministic if the cycle detector will
+ // detect the cycle upon the call edge A.<init> to B.<init>, or B.<init> to A.<init>. In order
+ // increase the likelihood that this test encounters both orderings, the test is repeated 5 times.
+ // Assuming that the chance of observing one of the two orderings is 50%, this test therefore has
+ // approximately 3% chance of succeeding although there is an issue.
+ @Test
+ public void testCallGraphCycle() throws Exception {
+ String main = "classmerging.CallGraphCycleTest";
+ Path[] programFiles =
+ new Path[] {
+ CF_DIR.resolve("CallGraphCycleTest.class"),
+ CF_DIR.resolve("CallGraphCycleTest$A.class"),
+ CF_DIR.resolve("CallGraphCycleTest$B.class")
+ };
+ Set<String> preservedClassNames =
+ ImmutableSet.of("classmerging.CallGraphCycleTest", "classmerging.CallGraphCycleTest$B");
+ for (int i = 0; i < 5; i++) {
+ runTest(main, programFiles, preservedClassNames::contains);
+ }
+ }
+
@Test
public void testConflictInGeneratedName() throws Exception {
String main = "classmerging.ConflictInGeneratedNameTest";
diff --git a/src/test/java/com/android/tools/r8/code/NativeMethodWithCodeTest.java b/src/test/java/com/android/tools/r8/code/NativeMethodWithCodeTest.java
index 83e711c..d524d7c 100644
--- a/src/test/java/com/android/tools/r8/code/NativeMethodWithCodeTest.java
+++ b/src/test/java/com/android/tools/r8/code/NativeMethodWithCodeTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.code;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -17,9 +17,9 @@
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/compatproguard/AtomicFieldUpdaterTest.java b/src/test/java/com/android/tools/r8/compatproguard/AtomicFieldUpdaterTest.java
index 502fa95..7d27c5e 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/AtomicFieldUpdaterTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/AtomicFieldUpdaterTest.java
@@ -12,9 +12,9 @@
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.smali.SmaliBuilder;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatProguardSmaliTestBase.java b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardSmaliTestBase.java
index 178337a..0d5849b 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatProguardSmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardSmaliTestBase.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliTestBase;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.nio.file.Path;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
index 5ef5830..3d9c912 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
@@ -12,9 +12,9 @@
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.smali.SmaliBuilder;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java b/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java
index 665919b..e478115 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java
@@ -15,9 +15,9 @@
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.smali.SmaliBuilder;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsLiveAtBlockEntryDebugTest.java b/src/test/java/com/android/tools/r8/debug/LocalsLiveAtBlockEntryDebugTest.java
index 36cdad0..d0f9d20 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsLiveAtBlockEntryDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsLiveAtBlockEntryDebugTest.java
@@ -7,9 +7,9 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.jasmin.JasminBuilder;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.Collections;
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index 2f995db..1bb357f 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -15,9 +15,9 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
index 4ce3b5a..aa8bc0c 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -19,8 +19,8 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
index d452699..f37b5ed 100644
--- a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -26,7 +26,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index a0b2515..7c4c250 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -20,9 +20,9 @@
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dexsplitter.DexSplitter.Options;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.io.FileNotFoundException;
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 6877617..8cba9a0 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -23,7 +23,7 @@
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.Collections;
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index 39c8d28..3987e0a 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -28,12 +28,12 @@
import com.android.tools.r8.utils.ArtErrorParser;
import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
import com.android.tools.r8.utils.ArtErrorParser.ArtErrorParserException;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
-import com.android.tools.r8.utils.DexInspector.FoundFieldSubject;
-import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FoundClassSubject;
+import com.android.tools.r8.utils.dexinspector.FoundFieldSubject;
+import com.android.tools.r8.utils.dexinspector.FoundMethodSubject;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import java.io.File;
diff --git a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
new file mode 100644
index 0000000..c011f6f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.invalid;
+
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.jasmin.JasminTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.google.common.collect.ImmutableList;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.Charset;
+import org.junit.Test;
+
+public class DuplicateDefinitionsTest extends JasminTestBase {
+
+ @Test
+ public void testDuplicateMethods() throws Exception {
+ JasminBuilder jasminBuilder = new JasminBuilder();
+ ClassBuilder classBuilder = jasminBuilder.addClass("C");
+ classBuilder.addMainMethod(".limit locals 1", ".limit stack 0", "return");
+ classBuilder.addMainMethod(".limit locals 1", ".limit stack 0", "return");
+ classBuilder.addVirtualMethod("method", "V", ".limit locals 1", ".limit stack 0", "return");
+ classBuilder.addVirtualMethod("method", "V", ".limit locals 1", ".limit stack 0", "return");
+
+ // Run D8 and intercept warnings.
+ PrintStream stderr = System.err;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setErr(new PrintStream(baos));
+
+ AndroidApp app = compileWithD8(jasminBuilder.build());
+
+ String output = new String(baos.toByteArray(), Charset.defaultCharset());
+ System.setOut(stderr);
+
+ // Check that warnings were emitted.
+ assertThat(
+ output,
+ containsString(
+ "Ignoring an implementation of the method `void C.main(java.lang.String[])` because "
+ + "it has multiple definitions"));
+ assertThat(
+ output,
+ containsString(
+ "Ignoring an implementation of the method `void C.method()` because "
+ + "it has multiple definitions"));
+
+ DexInspector inspector = new DexInspector(app);
+ ClassSubject clazz = inspector.clazz("C");
+ assertThat(clazz, isPresent());
+
+ // There are two direct methods, but only because one is <init>.
+ assertEquals(2, clazz.getDexClass().directMethods().length);
+ assertThat(clazz.method("void", "<init>", ImmutableList.of()), isPresent());
+
+ // There is only one virtual method.
+ assertEquals(1, clazz.getDexClass().virtualMethods().length);
+ }
+
+ @Test
+ public void testDuplicateFields() throws Exception {
+ JasminBuilder jasminBuilder = new JasminBuilder();
+ ClassBuilder classBuilder = jasminBuilder.addClass("C");
+ classBuilder.addField("public", "fld", "LC;", null);
+ classBuilder.addField("public", "fld", "LC;", null);
+ classBuilder.addStaticField("staticFld", "LC;", null);
+ classBuilder.addStaticField("staticFld", "LC;", null);
+
+ // Run D8 and intercept warnings.
+ PrintStream stderr = System.err;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setErr(new PrintStream(baos));
+
+ AndroidApp app = compileWithD8(jasminBuilder.build());
+
+ String output = new String(baos.toByteArray(), Charset.defaultCharset());
+ System.setOut(stderr);
+
+ // Check that warnings were emitted.
+ assertThat(output, containsString("Field `C C.fld` has multiple definitions"));
+ assertThat(output, containsString("Field `C C.staticFld` has multiple definitions"));
+
+ DexInspector inspector = new DexInspector(app);
+ ClassSubject clazz = inspector.clazz("C");
+ assertThat(clazz, isPresent());
+
+ // Redundant fields have been removed.
+ assertEquals(1, clazz.getDexClass().instanceFields().length);
+ assertEquals(1, clazz.getDexClass().staticFields().length);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index d4ab6c6..f02098a 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -20,9 +20,9 @@
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.io.IOException;
import java.util.List;
import java.util.ListIterator;
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index 16785f6..de97052 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -25,18 +25,18 @@
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.NonNull;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.NonNullTracker;
import com.android.tools.r8.ir.optimize.nonnull.FieldAccessTest;
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterArrayAccess;
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterFieldAccess;
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
-import com.android.tools.r8.ir.optimize.NonNullTracker;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.function.BiConsumer;
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
index aefe665..286fb56 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
@@ -30,10 +30,10 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Smali;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.nio.charset.StandardCharsets;
diff --git a/src/test/java/com/android/tools/r8/ir/callgraph/CycleEliminationTest.java b/src/test/java/com/android/tools/r8/ir/callgraph/CycleEliminationTest.java
new file mode 100644
index 0000000..56699e3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/callgraph/CycleEliminationTest.java
@@ -0,0 +1,214 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.callgraph;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.CallGraph.CycleEliminator;
+import com.android.tools.r8.ir.conversion.CallGraph.Node;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BooleanSupplier;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class CycleEliminationTest extends TestBase {
+
+ private static class Configuration {
+
+ final Collection<Node> nodes;
+ final Set<Node> forceInline;
+ final BooleanSupplier test;
+
+ Configuration(Collection<Node> nodes, Set<Node> forceInline, BooleanSupplier test) {
+ this.nodes = nodes;
+ this.forceInline = forceInline;
+ this.test = test;
+ }
+ }
+
+ private DexItemFactory dexItemFactory = new DexItemFactory();
+
+ @Rule public final ExpectedException exception = ExpectedException.none();
+
+ @Test
+ public void testSimpleCycle() {
+ Node method = createNode("n1");
+ Node forceInlinedMethod = createForceInlinedNode("n2");
+
+ Iterable<Collection<Node>> orderings =
+ ImmutableList.of(
+ ImmutableList.of(method, forceInlinedMethod),
+ ImmutableList.of(forceInlinedMethod, method));
+
+ for (Collection<Node> nodes : orderings) {
+ // Create a cycle between the two nodes.
+ method.addCallee(forceInlinedMethod);
+ forceInlinedMethod.addCallee(method);
+
+ // Check that the cycle eliminator finds the cycle.
+ CycleEliminator cycleEliminator = new CycleEliminator(nodes, new InternalOptions());
+ assertEquals(1, cycleEliminator.breakCycles());
+
+ // The edge from method to forceInlinedMethod should be removed to ensure that force inlining
+ // will work.
+ assertTrue(forceInlinedMethod.isLeaf());
+
+ // Check that the cycle eliminator agrees that there are no more cycles left.
+ assertEquals(0, cycleEliminator.breakCycles());
+ }
+ }
+
+ @Test
+ public void testSimpleCycleWithCyclicForceInlining() {
+ Node method = createForceInlinedNode("n1");
+ Node forceInlinedMethod = createForceInlinedNode("n2");
+
+ // Create a cycle between the two nodes.
+ method.addCallee(forceInlinedMethod);
+ forceInlinedMethod.addCallee(method);
+
+ CycleEliminator cycleEliminator =
+ new CycleEliminator(ImmutableList.of(method, forceInlinedMethod), new InternalOptions());
+
+ exception.expect(CompilationError.class);
+ exception.expectMessage(CycleEliminator.CYCLIC_FORCE_INLINING_MESSAGE);
+
+ // Should throw because force inlining will fail.
+ cycleEliminator.breakCycles();
+ }
+
+ @Test
+ public void testGraphWithNestedCycles() {
+ Node n1 = createNode("n1");
+ Node n2 = createNode("n2");
+ Node n3 = createNode("n3");
+
+ BooleanSupplier canInlineN1 =
+ () -> {
+ // The node n1 should be force inlined into n2 and n3, so these edges must be kept.
+ assertTrue(n2.hasCallee(n1));
+ assertTrue(n3.hasCallee(n1));
+ // Furthermore, the edge from n1 to n2 must be removed.
+ assertFalse(n1.hasCallee(n2));
+ return true;
+ };
+
+ BooleanSupplier canInlineN3 =
+ () -> {
+ // The node n3 should be force inlined into n2, so this edge must be kept.
+ assertTrue(n2.hasCallee(n3));
+ // Furthermore, one of the edges n1 -> n2 and n3 -> n1 must be removed.
+ assertFalse(n1.hasCallee(n2) && n3.hasCallee(n1));
+ return true;
+ };
+
+ BooleanSupplier canInlineN1N3 =
+ () -> {
+ // The edge n1 -> n2 must be removed.
+ assertFalse(n1.hasCallee(n2));
+ // Check that both can be force inlined.
+ return canInlineN1.getAsBoolean() && canInlineN3.getAsBoolean();
+ };
+
+ List<Collection<Node>> orderings =
+ ImmutableList.of(
+ ImmutableList.of(n1, n2, n3),
+ ImmutableList.of(n1, n3, n2),
+ ImmutableList.of(n2, n1, n3),
+ ImmutableList.of(n2, n3, n1),
+ ImmutableList.of(n3, n1, n2),
+ ImmutableList.of(n3, n2, n1));
+
+ List<Configuration> configurations = new ArrayList<>();
+ // All orderings, where no methods are marked as force inline.
+ orderings
+ .stream()
+ .map(ordering -> new Configuration(ordering, ImmutableSet.of(), null))
+ .forEach(configurations::add);
+ // All orderings, where n1 is marked as force inline
+ // (the configuration where n2 is marked as force inline is symmetric).
+ orderings
+ .stream()
+ .map(ordering -> new Configuration(ordering, ImmutableSet.of(n1), canInlineN1))
+ .forEach(configurations::add);
+ // All orderings, where n3 is marked as force inline.
+ orderings
+ .stream()
+ .map(ordering -> new Configuration(ordering, ImmutableSet.of(n3), canInlineN3))
+ .forEach(configurations::add);
+ // All orderings, where n1 and n3 are marked as force inline.
+ orderings
+ .stream()
+ .map(ordering -> new Configuration(ordering, ImmutableSet.of(n1, n3), canInlineN1N3))
+ .forEach(configurations::add);
+
+ for (Configuration configuration : configurations) {
+ // Create a cycle between the three nodes.
+ n1.addCallee(n2);
+ n2.addCallee(n3);
+ n3.addCallee(n1);
+
+ // Create a cycle in the graph between node n1 and n2.
+ n2.addCallee(n1);
+
+ for (Node node : configuration.nodes) {
+ if (configuration.forceInline.contains(node)) {
+ node.method.markForceInline();
+ } else {
+ node.method.unsetForceInline();
+ }
+ }
+
+ // Check that the cycle eliminator finds the cycles.
+ CycleEliminator cycleEliminator =
+ new CycleEliminator(configuration.nodes, new InternalOptions());
+ int numberOfCycles = cycleEliminator.breakCycles();
+ if (numberOfCycles == 1) {
+ // If only one cycle was removed, then it must be the edge from n1 -> n2 that was removed.
+ assertTrue(n1.isLeaf());
+ } else {
+ // Check that the cycle eliminator found both cycles.
+ assertEquals(2, numberOfCycles);
+ }
+
+ // Check that the cycle eliminator agrees that there are no more cycles left.
+ assertEquals(0, cycleEliminator.breakCycles());
+
+ // Check that force inlining is guaranteed to succeed.
+ if (configuration.test != null) {
+ assertTrue(configuration.test.getAsBoolean());
+ }
+ }
+ }
+
+ private Node createNode(String methodName) {
+ DexMethod signature =
+ dexItemFactory.createMethod(
+ dexItemFactory.objectType,
+ dexItemFactory.createProto(dexItemFactory.voidType),
+ methodName);
+ return new Node(new DexEncodedMethod(signature, null, null, null, null));
+ }
+
+ private Node createForceInlinedNode(String methodName) {
+ Node node = createNode(methodName);
+ node.method.markForceInline();
+ return node;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
index 1f4d2ba..6aecc60 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
@@ -4,14 +4,16 @@
package com.android.tools.r8.ir.desugar.annotations;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThat;
import com.android.tools.r8.AsmTestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import java.util.Collections;
import org.junit.Assert;
import org.junit.Test;
@@ -156,44 +158,44 @@
DexInspector inspector = new DexInspector(output);
// Get classes A, B, and C.
- DexInspector.ClassSubject clazzA = inspector.clazz(A.class.getCanonicalName());
+ ClassSubject clazzA = inspector.clazz(A.class.getCanonicalName());
assertThat(clazzA, isPresent());
- DexInspector.ClassSubject clazzB = inspector.clazz(B.class.getCanonicalName());
+ ClassSubject clazzB = inspector.clazz(B.class.getCanonicalName());
assertThat(clazzB, isPresent());
- DexInspector.ClassSubject clazzC = inspector.clazz(C.class.getCanonicalName());
+ ClassSubject clazzC = inspector.clazz(C.class.getCanonicalName());
assertThat(clazzC, isPresent());
// Check that the original methods are there, and that they are not synthetic.
- DexInspector.MethodSubject methodA =
+ MethodSubject methodA =
clazzB.method(A.class.getCanonicalName(), "method", Collections.emptyList());
assertThat(methodA, isPresent());
Assert.assertTrue(!methodA.getMethod().isSyntheticMethod());
- DexInspector.MethodSubject methodB =
+ MethodSubject methodB =
clazzB.method(A.class.getCanonicalName(), "method", Collections.emptyList());
assertThat(methodB, isPresent());
Assert.assertTrue(!methodB.getMethod().isSyntheticMethod());
- DexInspector.MethodSubject methodC =
+ MethodSubject methodC =
clazzC.method(A.class.getCanonicalName(), "method", Collections.emptyList());
assertThat(methodC, isPresent());
Assert.assertTrue(!methodC.getMethod().isSyntheticMethod());
// Check that a synthetic method has been added to class B.
- DexInspector.MethodSubject methodB2 =
+ MethodSubject methodB2 =
clazzB.method(B.class.getCanonicalName(), "method", Collections.emptyList());
assertThat(methodB2, isPresent());
Assert.assertTrue(methodB2.getMethod().isSyntheticMethod());
// Check that two synthetic methods have been added to class C.
- DexInspector.MethodSubject methodC2 =
+ MethodSubject methodC2 =
clazzC.method(B.class.getCanonicalName(), "method", Collections.emptyList());
assertThat(methodC2, isPresent());
Assert.assertTrue(methodC2.getMethod().isSyntheticMethod());
- DexInspector.MethodSubject methodC3 =
+ MethodSubject methodC3 =
clazzC.method(C.class.getCanonicalName(), "method", Collections.emptyList());
assertThat(methodC3, isPresent());
Assert.assertTrue(methodC3.getMethod().isSyntheticMethod());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/B87341268.java b/src/test/java/com/android/tools/r8/ir/optimize/B87341268.java
index 3f7bcfb..11c7c0f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/B87341268.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/B87341268.java
@@ -3,13 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertThat;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import org.junit.Test;
public class B87341268 extends TestBase {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index a5c7093..5a2bc22 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -18,9 +18,9 @@
import com.android.tools.r8.code.SputObject;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index 7c90976..2ef43fd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -26,9 +26,9 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.util.function.Consumer;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 5dc7f23..8e78784 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -28,10 +28,10 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Files;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
index 525e21b..43b728e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
@@ -17,7 +17,7 @@
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 3239191..d40e202 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.ir.optimize.classinliner.code.C;
import com.android.tools.r8.ir.optimize.classinliner.code.CodeTestClass;
import com.android.tools.r8.ir.optimize.classinliner.invalidroot.InvalidRootsTestClass;
+import com.android.tools.r8.ir.optimize.classinliner.lambdas.LambdasTestClass;
import com.android.tools.r8.ir.optimize.classinliner.trivial.ClassWithFinal;
import com.android.tools.r8.ir.optimize.classinliner.trivial.CycleReferenceAB;
import com.android.tools.r8.ir.optimize.classinliner.trivial.CycleReferenceBA;
@@ -44,9 +45,9 @@
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.Sets;
import java.nio.file.Path;
import java.util.Collections;
@@ -301,6 +302,37 @@
assertFalse(inspector.clazz(InvalidRootsTestClass.B.class).isPresent());
}
+ @Test
+ public void testDesugaredLambdas() throws Exception {
+ byte[][] classes = {
+ ToolHelper.getClassAsBytes(LambdasTestClass.class),
+ ToolHelper.getClassAsBytes(LambdasTestClass.Iface.class),
+ ToolHelper.getClassAsBytes(LambdasTestClass.IfaceUtil.class),
+ };
+ AndroidApp app = runR8(buildAndroidApp(classes), LambdasTestClass.class);
+
+ String javaOutput = runOnJava(LambdasTestClass.class);
+ String artOutput = runOnArt(app, LambdasTestClass.class);
+ assertEquals(javaOutput, artOutput);
+
+ DexInspector inspector = new DexInspector(app);
+ ClassSubject clazz = inspector.clazz(LambdasTestClass.class);
+
+ assertEquals(
+ Sets.newHashSet(
+ "java.lang.StringBuilder"),
+ collectTypes(clazz, "testStatelessLambda", "void"));
+
+ assertEquals(
+ Sets.newHashSet(
+ "java.lang.StringBuilder"),
+ collectTypes(clazz, "testStatefulLambda", "void", "java.lang.String", "java.lang.String"));
+
+ assertEquals(0,
+ inspector.allClasses().stream()
+ .filter(ClassSubject::isSynthesizedJavaLambdaClass).count());
+ }
+
private Set<String> collectTypes(
ClassSubject clazz, String methodName, String retValue, String... params) {
return Stream.concat(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/lambdas/LambdasTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/lambdas/LambdasTestClass.java
new file mode 100644
index 0000000..130f895
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/lambdas/LambdasTestClass.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.classinliner.lambdas;
+
+public class LambdasTestClass {
+ private static int ID = 0;
+
+ private static int nextInt() {
+ return ID++;
+ }
+
+ private static String next() {
+ return Integer.toString(nextInt());
+ }
+
+ public interface Iface {
+ String foo();
+ }
+
+ public static class IfaceUtil {
+ public static void act(Iface iface) {
+ System.out.println("" + next() + "> " + iface.foo());
+ }
+ }
+
+ public static void main(String[] args) {
+ LambdasTestClass test = new LambdasTestClass();
+ test.testStatelessLambda();
+ test.testStatefulLambda(next(), next());
+ }
+
+ public static String exact() {
+ return next();
+ }
+
+ public static String almost(String... s) {
+ return next();
+ }
+
+ private synchronized void testStatelessLambda() {
+ IfaceUtil.act(() -> next());
+ IfaceUtil.act(LambdasTestClass::next);
+ IfaceUtil.act(LambdasTestClass::exact);
+ IfaceUtil.act(LambdasTestClass::almost);
+ }
+
+ private synchronized void testStatefulLambda(String a, String b) {
+ IfaceUtil.act(() -> a);
+ IfaceUtil.act(() -> a + b);
+ IfaceUtil.act((a + b)::toLowerCase);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeInterfaceToInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeInterfaceToInvokeVirtualTest.java
index 71dae86..0ae7971 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeInterfaceToInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeInterfaceToInvokeVirtualTest.java
@@ -24,8 +24,8 @@
import com.android.tools.r8.ir.optimize.devirtualize.invokeinterface.Main;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java
index adb87e6..4b603952 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlinerTest.java
@@ -21,10 +21,10 @@
import com.android.tools.r8.ir.optimize.inliner.interfaces.InterfaceTargetsTestClass.IfaceNoImpl;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import java.nio.file.Path;
import java.util.stream.Collectors;
import java.util.stream.Stream;
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java b/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
index a143b65..c79e50e 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/B77240639.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.regalloc;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
@@ -11,8 +11,8 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.util.List;
import java.util.Map;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/B79405526.java b/src/test/java/com/android/tools/r8/ir/regalloc/B79405526.java
index 08058e5..36156eb 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/B79405526.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/B79405526.java
@@ -4,12 +4,13 @@
package com.android.tools.r8.ir.regalloc;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertThat;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import org.junit.Test;
public class B79405526 extends TestBase {
@@ -17,7 +18,7 @@
public void test() throws Exception {
AndroidApp app = compileWithD8(readClasses(TestClass.class));
DexInspector inspector = new DexInspector(app);
- DexInspector.ClassSubject clazz = inspector.clazz(TestClass.class);
+ ClassSubject clazz = inspector.clazz(TestClass.class);
assertThat(clazz, isPresent());
// Throws if a method in TestClass does not verify.
runOnArt(app, TestClass.class.getName());
diff --git a/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java
index 16fed9e..d8861b6 100644
--- a/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java
+++ b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.utils.ArtErrorParser;
import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
import com.android.tools.r8.utils.ArtErrorParser.ArtErrorParserException;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
diff --git a/src/test/java/com/android/tools/r8/jasmin/AnnotationCompanionClassTest.java b/src/test/java/com/android/tools/r8/jasmin/AnnotationCompanionClassTest.java
index 033183c..ed9194e 100644
--- a/src/test/java/com/android/tools/r8/jasmin/AnnotationCompanionClassTest.java
+++ b/src/test/java/com/android/tools/r8/jasmin/AnnotationCompanionClassTest.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
index e572bfa..b56824d 100644
--- a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -12,9 +12,9 @@
import com.android.tools.r8.jasmin.JasminBuilder.ClassFileVersion;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java b/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java
index 27d8d8b..c7a32b3 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.LinkedHashMap;
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index 7b74453..c2dd7b1 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -14,9 +14,9 @@
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.DexInspector;
-import com.android.tools.r8.utils.DexInspector.AnnotationSubject;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.AnnotationSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index fd78cb7..4fd1d8d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -21,12 +21,12 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index dc2774a..a28d0c5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -17,8 +17,8 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
index 062095c..658e89a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -13,8 +13,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.ArrayList;
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java
index 2beb777..233bb24 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java
@@ -19,8 +19,8 @@
import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Lambda;
import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Verifier;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Files;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 7425082..53b7925 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -16,9 +16,9 @@
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
import java.nio.file.Path;
import java.util.Collections;
import org.junit.Assert;
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 73d1e50..45f74af 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -7,10 +7,10 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import java.util.Collections;
import java.util.function.Consumer;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index 021e045..c764974 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -5,8 +5,8 @@
package com.android.tools.r8.kotlin;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import java.util.Collections;
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index dc9b642..03c2873 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -10,10 +10,10 @@
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
import java.util.Map.Entry;
import java.util.function.Consumer;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 5a30cfd..fd41344 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -10,9 +10,9 @@
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 101bf06..dc5347e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -59,8 +59,6 @@
import com.android.tools.r8.utils.AndroidAppConsumers;
import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
@@ -68,6 +66,8 @@
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FoundClassSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
index 0f1359e..fea8160 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
@@ -23,7 +23,7 @@
import com.android.tools.r8.VmTestRunner;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Paths;
import java.util.ArrayList;
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java b/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
index aba14cf..d3805d9 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.memberrebinding;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -11,15 +11,15 @@
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
index 46a439d..c2ec4b7 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -11,12 +11,12 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.FieldAccessInstructionSubject;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.TestDescriptionWatcher;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldAccessInstructionSubject;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
+import com.android.tools.r8.utils.dexinspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
index 3429c2a..9b120e6 100644
--- a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
+++ b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
@@ -13,10 +13,10 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Iterator;
import java.util.function.Consumer;
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
index bf6c895..876a758 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -21,11 +21,11 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
+import com.android.tools.r8.utils.dexinspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
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 f27c88a..58e17d1 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -20,10 +20,10 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.nio.file.Path;
@@ -37,9 +37,7 @@
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
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 982e935..41e514a 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
@@ -26,9 +26,9 @@
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java b/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
index b9ff7ea..e081f18 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
@@ -4,10 +4,9 @@
package com.android.tools.r8.naming;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isRenamed;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.not;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -21,6 +20,7 @@
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_8;
+import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DiagnosticsChecker;
import com.android.tools.r8.R8Command;
@@ -29,15 +29,21 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.invokesuper.Consumer;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.Collection;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
+@RunWith(Parameterized.class)
public class MinifierClassSignatureTest extends TestBase {
/*
@@ -62,6 +68,16 @@
String outerSignature = "<T:Ljava/lang/Object;>Ljava/lang/Object;";
String extendsInnerSignature = "LOuter<TT;>.Inner;";
String extendsInnerInnerSignature = "LOuter<TT;>.Inner.InnerInner;";
+ Backend backend;
+
+ @Parameters(name = "Backend: {0}")
+ public static Collection<Backend> data() {
+ return Arrays.asList(Backend.values());
+ }
+
+ public MinifierClassSignatureTest(Backend backend) {
+ this.backend = backend;
+ }
private byte[] dumpSimple(String classSignature) throws Exception {
@@ -290,26 +306,36 @@
Consumer<DexInspector> inspect)
throws Exception {
DiagnosticsChecker checker = new DiagnosticsChecker();
- DexInspector inspector = new DexInspector(
- ToolHelper.runR8(R8Command.builder(checker)
- .addClassProgramData(dumpSimple(signatures.get("Simple")), Origin.unknown())
- .addClassProgramData(dumpBase(signatures.get("Base")), Origin.unknown())
- .addClassProgramData(dumpOuter(signatures.get("Outer")), Origin.unknown())
- .addClassProgramData(dumpInner(signatures.get("Outer$Inner")), Origin.unknown())
- .addClassProgramData(
- dumpExtendsInner(signatures.get("Outer$ExtendsInner")), Origin.unknown())
- .addClassProgramData(
- dumpInnerInner(signatures.get("Outer$Inner$InnerInner")), Origin.unknown())
- .addClassProgramData(
- dumpExtendsInnerInner(
- signatures.get("Outer$Inner$ExtendsInnerInner")), Origin.unknown())
- .addProguardConfiguration(ImmutableList.of(
- "-keepattributes InnerClasses,EnclosingMethod,Signature",
- "-keep,allowobfuscation class **"
- ), Origin.unknown())
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
- .setProguardMapConsumer(StringConsumer.emptyConsumer())
- .build()));
+ assert (backend == Backend.CF || backend == Backend.DEX);
+ DexInspector inspector =
+ new DexInspector(
+ ToolHelper.runR8(
+ R8Command.builder(checker)
+ .addClassProgramData(dumpSimple(signatures.get("Simple")), Origin.unknown())
+ .addClassProgramData(dumpBase(signatures.get("Base")), Origin.unknown())
+ .addClassProgramData(dumpOuter(signatures.get("Outer")), Origin.unknown())
+ .addClassProgramData(dumpInner(signatures.get("Outer$Inner")), Origin.unknown())
+ .addClassProgramData(
+ dumpExtendsInner(signatures.get("Outer$ExtendsInner")), Origin.unknown())
+ .addClassProgramData(
+ dumpInnerInner(signatures.get("Outer$Inner$InnerInner")), Origin.unknown())
+ .addClassProgramData(
+ dumpExtendsInnerInner(signatures.get("Outer$Inner$ExtendsInnerInner")),
+ Origin.unknown())
+ .addProguardConfiguration(
+ ImmutableList.of(
+ "-keepattributes InnerClasses,EnclosingMethod,Signature",
+ "-keep,allowobfuscation class **"),
+ Origin.unknown())
+ .setProgramConsumer(
+ backend == Backend.DEX
+ ? DexIndexedConsumer.emptyConsumer()
+ : ClassFileConsumer.emptyConsumer())
+ .setProguardMapConsumer(StringConsumer.emptyConsumer())
+ .build(),
+ options -> {
+ options.testing.suppressExperimentalCfBackendWarning = true;
+ }));
// All classes are kept, and renamed.
assertThat(inspector.clazz("Simple"), isRenamed());
assertThat(inspector.clazz("Base"), isRenamed());
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierFieldSignatureTest.java b/src/test/java/com/android/tools/r8/naming/MinifierFieldSignatureTest.java
index e48ce56..02ec163 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierFieldSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierFieldSignatureTest.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.naming;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isRenamed;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -21,6 +21,7 @@
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_8;
+import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DiagnosticsChecker;
import com.android.tools.r8.R8Command;
@@ -29,18 +30,23 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.invokesuper.Consumer;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Map;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
-
+@RunWith(Parameterized.class)
public class MinifierFieldSignatureTest extends TestBase {
/*
@@ -59,6 +65,16 @@
private String anArrayOfXSignature = "[TX;";
private String aFieldsOfXSignature = "LFields<TX;>;";
private String aFieldsOfXInnerSignature = "LFields<TX;>.Inner;";
+ private Backend backend;
+
+ @Parameters(name = "Backend: {0}")
+ public static Collection<Backend> data() {
+ return Arrays.asList(Backend.values());
+ }
+
+ public MinifierFieldSignatureTest(Backend backend) {
+ this.backend = backend;
+ }
public byte[] dumpFields(Map<String, String> signatures) throws Exception {
@@ -164,17 +180,27 @@
Consumer<DexInspector> inspect)
throws Exception {
DiagnosticsChecker checker = new DiagnosticsChecker();
- DexInspector inspector = new DexInspector(
- ToolHelper.runR8(R8Command.builder(checker)
- .addClassProgramData(dumpFields(signatures), Origin.unknown())
- .addClassProgramData(dumpInner(), Origin.unknown())
- .addProguardConfiguration(ImmutableList.of(
- "-keepattributes InnerClasses,EnclosingMethod,Signature",
- "-keep,allowobfuscation class ** { *; }"
- ), Origin.unknown())
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
- .setProguardMapConsumer(StringConsumer.emptyConsumer())
- .build()));
+ assert (backend == Backend.CF || backend == Backend.DEX);
+ DexInspector inspector =
+ new DexInspector(
+ ToolHelper.runR8(
+ R8Command.builder(checker)
+ .addClassProgramData(dumpFields(signatures), Origin.unknown())
+ .addClassProgramData(dumpInner(), Origin.unknown())
+ .addProguardConfiguration(
+ ImmutableList.of(
+ "-keepattributes InnerClasses,EnclosingMethod,Signature",
+ "-keep,allowobfuscation class ** { *; }"),
+ Origin.unknown())
+ .setProgramConsumer(
+ backend == Backend.DEX
+ ? DexIndexedConsumer.emptyConsumer()
+ : ClassFileConsumer.emptyConsumer())
+ .setProguardMapConsumer(StringConsumer.emptyConsumer())
+ .build(),
+ options -> {
+ options.testing.suppressExperimentalCfBackendWarning = true;
+ }));
// All classes are kept, and renamed.
ClassSubject clazz = inspector.clazz("Fields");
assertThat(clazz, isRenamed());
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierMethodSignatureTest.java b/src/test/java/com/android/tools/r8/naming/MinifierMethodSignatureTest.java
index 41750ae..efc9855 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierMethodSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierMethodSignatureTest.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.naming;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isRenamed;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -24,6 +24,7 @@
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_8;
+import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DiagnosticsChecker;
import com.android.tools.r8.R8Command;
@@ -32,17 +33,23 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.invokesuper.Consumer;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Map;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
+@RunWith(Parameterized.class)
public class MinifierMethodSignatureTest extends TestBase {
/*
@@ -61,6 +68,16 @@
private String parameterizedReturnSignature = "()LMethods<TX;>.Inner;";
private String parameterizedArgumentsSignature = "(TX;LMethods<TX;>.Inner;)V";
private String parametrizedThrowsSignature = "()V^TX;";
+ Backend backend;
+
+ @Parameters(name = "Backend: {0}")
+ public static Collection<Backend> data() {
+ return Arrays.asList(Backend.values());
+ }
+
+ public MinifierMethodSignatureTest(Backend backend) {
+ this.backend = backend;
+ }
private byte[] dumpMethods(Map<String, String> signatures) throws Exception {
@@ -185,17 +202,27 @@
Consumer<DexInspector> inspect)
throws Exception {
DiagnosticsChecker checker = new DiagnosticsChecker();
- DexInspector inspector = new DexInspector(
- ToolHelper.runR8(R8Command.builder(checker)
- .addClassProgramData(dumpMethods(signatures), Origin.unknown())
- .addClassProgramData(dumpInner(), Origin.unknown())
- .addProguardConfiguration(ImmutableList.of(
- "-keepattributes InnerClasses,EnclosingMethod,Signature",
- "-keep,allowobfuscation class ** { *; }"
- ), Origin.unknown())
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
- .setProguardMapConsumer(StringConsumer.emptyConsumer())
- .build()));
+ assert (backend == Backend.CF || backend == Backend.DEX);
+ DexInspector inspector =
+ new DexInspector(
+ ToolHelper.runR8(
+ R8Command.builder(checker)
+ .addClassProgramData(dumpMethods(signatures), Origin.unknown())
+ .addClassProgramData(dumpInner(), Origin.unknown())
+ .addProguardConfiguration(
+ ImmutableList.of(
+ "-keepattributes InnerClasses,EnclosingMethod,Signature",
+ "-keep,allowobfuscation class ** { *; }"),
+ Origin.unknown())
+ .setProgramConsumer(
+ backend == Backend.DEX
+ ? DexIndexedConsumer.emptyConsumer()
+ : ClassFileConsumer.emptyConsumer())
+ .setProguardMapConsumer(StringConsumer.emptyConsumer())
+ .build(),
+ options -> {
+ options.testing.suppressExperimentalCfBackendWarning = true;
+ }));
// All classes are kept, and renamed.
ClassSubject clazz = inspector.clazz("Methods");
assertThat(clazz, isRenamed());
diff --git a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
index ddd0aa2..25e2358 100644
--- a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
+++ b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -19,10 +19,10 @@
import com.android.tools.r8.VmTestRunner;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.KeepingDiagnosticHandler;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
index ec27895..4e46637 100644
--- a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
+++ b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.naming.b72391662;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -17,9 +17,9 @@
import com.android.tools.r8.naming.b72391662.subpackage.OtherPackageTestClass;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java b/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java
index 008e4ff..1b7d842 100644
--- a/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java
+++ b/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.naming.b80083341;
import static com.android.tools.r8.utils.DescriptorUtils.getClassNameFromDescriptor;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -12,8 +12,8 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.VmTestRunner;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
index a9eb622..0feddfd 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -18,8 +18,8 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
index 3e5ba7c..6e3e30a 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
@@ -14,10 +14,10 @@
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.jasmin.JasminTestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java b/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
index c929eaf..8ae31f7 100644
--- a/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
+++ b/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
@@ -14,11 +14,12 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInstructionSubject;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
+import com.android.tools.r8.utils.dexinspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Iterator;
import java.util.function.BiConsumer;
@@ -82,7 +83,8 @@
assertTrue(insn.isInvoke());
assertTrue(((InvokeInstructionSubject) insn)
.invokedMethod().name.toString().equals("throwNpe"));
- assertTrue(nextInstruction(instructions).isConst4());
+ insn = nextInstruction(instructions);
+ assertTrue(insn instanceof DexInstructionSubject && ((DexInstructionSubject) insn).isConst4());
assertTrue(nextInstruction(instructions).isThrow());
assertFalse(instructions.hasNext());
@@ -105,7 +107,8 @@
assertTrue(((InvokeInstructionSubject) insn)
.invokedMethod().name.toString().equals("throwNpe"));
}
- assertTrue(nextInstruction(instructions).isConst4());
+ insn = nextInstruction(instructions);
+ assertTrue(insn instanceof DexInstructionSubject && ((DexInstructionSubject) insn).isConst4());
assertTrue(nextInstruction(instructions).isThrow());
assertFalse(instructions.hasNext());
@@ -119,7 +122,8 @@
assertTrue(insn.isInvoke());
assertTrue(((InvokeInstructionSubject) insn)
.invokedMethod().name.toString().equals("innerNotReachable"));
- assertTrue(nextInstruction(instructions).isConst4());
+ insn = nextInstruction(instructions);
+ assertTrue(insn instanceof DexInstructionSubject && ((DexInstructionSubject) insn).isConst4());
assertTrue(nextInstruction(instructions).isThrow());
assertFalse(instructions.hasNext());
}
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
index 0a0e861..68f6fe0 100644
--- a/src/test/java/com/android/tools/r8/regress/B76025099.java
+++ b/src/test/java/com/android/tools/r8/regress/B76025099.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.regress;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -19,10 +19,10 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.nio.file.Files;
diff --git a/src/test/java/com/android/tools/r8/regress/Regress37740372.java b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
index 64df6ed..1d2c6fe 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress37740372.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
@@ -18,8 +18,8 @@
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.util.Base64;
import java.util.Set;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java b/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java
index 90fba5f..abf43b2 100644
--- a/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java
+++ b/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.regress.b111250398;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -15,11 +15,11 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import org.junit.Test;
@@ -91,6 +91,16 @@
t = f;
}
+ public void mfWithMonitor() {
+ t = f;
+ synchronized (this) {
+ t = f;
+ t = f;
+ }
+ t = f;
+ t = f;
+ }
+
public void msf() {
t = sf;
t = sf;
@@ -231,6 +241,8 @@
assertThat(classA, isPresent());
MethodSubject mfOnA = classA.method("void", "mf", ImmutableList.of());
assertThat(mfOnA, isPresent());
+ MethodSubject mfWithMonitorOnA = classA.method("void", "mfWithMonitor", ImmutableList.of());
+ assertThat(mfWithMonitorOnA, isPresent());
MethodSubject msfOnA = classA.method("void", "msf", ImmutableList.of());
assertThat(msfOnA, isPresent());
MethodSubject mvOnA = classA.method("void", "mv", ImmutableList.of());
@@ -264,6 +276,13 @@
// compilation (R8) will eliminate field loads on non-volatile fields.
assertEquals(1, countIget(mfOnA.getMethod().getCode().asDexCode(), fOnA.getField().field));
assertEquals(1, countSget(msfOnA.getMethod().getCode().asDexCode(), sfOnA.getField().field));
+ // TODO(111380066). This could be 2 in stead of 4, but right now the optimization tracks the
+ // combined set of fields for all successors, and for synchronized code all blocks have
+ // exceptional edges for ensuring monitor exit causing the active load to be invalidated for
+ // both normal and exceptional successors.
+ assertEquals(4,
+ countIget(mfWithMonitorOnA.getMethod().getCode().asDexCode(), fOnA.getField().field));
+
// For fields on other class both separate compilation (D8) and whole program
// compilation (R8) will differ in the eliminated field loads of non-volatile fields.
assertEquals(mfOnBGets,
@@ -283,9 +302,7 @@
public void testWholeProgram() throws Exception {
DexInspector inspector =
new DexInspector(compileWithR8(readClasses(A.class, B.class), this::releaseMode));
- // The reason for getting two Igets in B.mf is that the first Iget inserts a NonNull
- // instruction which creates a new value for the remaining Igets.
- check(inspector, 2, 1);
+ check(inspector, 1, 1);
}
private void checkMixed(AndroidApp app) throws Exception{
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
index a185f4c..9cc17af 100644
--- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -13,8 +13,8 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FoundClassSubject;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/regress/b77496850/B77496850.java b/src/test/java/com/android/tools/r8/regress/b77496850/B77496850.java
index 03614b9..448e472 100644
--- a/src/test/java/com/android/tools/r8/regress/b77496850/B77496850.java
+++ b/src/test/java/com/android/tools/r8/regress/b77496850/B77496850.java
@@ -15,9 +15,9 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/resolution/B77944861.java b/src/test/java/com/android/tools/r8/resolution/B77944861.java
index aa5552a..a35adbc 100644
--- a/src/test/java/com/android/tools/r8/resolution/B77944861.java
+++ b/src/test/java/com/android/tools/r8/resolution/B77944861.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.resolution;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -19,10 +19,10 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/ChromuimAssertionHookMock.java b/src/test/java/com/android/tools/r8/rewrite/assertions/ChromuimAssertionHookMock.java
new file mode 100644
index 0000000..772ef09
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/ChromuimAssertionHookMock.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions;
+
+public class ChromuimAssertionHookMock {
+ public static void assertFailureHandler(AssertionError assertion) {
+ System.out.println("Got AssertionError " + assertion);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/ClassWithAssertions.java b/src/test/java/com/android/tools/r8/rewrite/assertions/ClassWithAssertions.java
index cc56790..49fe85e 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/ClassWithAssertions.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/ClassWithAssertions.java
@@ -16,7 +16,9 @@
}
int getX() {
+ System.out.println("1");
assert condition();
+ System.out.println("2");
return x;
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
index 3c724c0..ca62dee 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
@@ -4,17 +4,139 @@
package com.android.tools.r8.rewrite.assertions;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.function.Consumer;
+import java.util.function.Function;
import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+// This ASM class visitor has been adapted from
+// https://chromium.googlesource.com/chromium/src/+/164e81fcd0828b40f5496e9025349ea728cde7f5/build/android/bytecode/java/org/chromium/bytecode/AssertionEnablerClassAdapter.java
+// See b/110887293.
+
+/**
+ * An ClassVisitor for replacing Java ASSERT statements with a function by modifying Java bytecode.
+ *
+ * We do this in two steps, first step is to enable assert.
+ * Following bytecode is generated for each class with ASSERT statements:
+ * 0: ldc #8 // class CLASSNAME
+ * 2: invokevirtual #9 // Method java/lang/Class.desiredAssertionStatus:()Z
+ * 5: ifne 12
+ * 8: iconst_1
+ * 9: goto 13
+ * 12: iconst_0
+ * 13: putstatic #2 // Field $assertionsDisabled:Z
+ * Replaces line #13 to the following:
+ * 13: pop
+ * Consequently, $assertionsDisabled is assigned the default value FALSE.
+ * This is done in the first if statement in overridden visitFieldInsn. We do this per per-assert.
+ *
+ * Second step is to replace assert statement with a function:
+ * The followed instructions are generated by a java assert statement:
+ * getstatic #3 // Field $assertionsDisabled:Z
+ * ifne 118 // Jump to instruction as if assertion if not enabled
+ * ...
+ * ifne 19
+ * new #4 // class java/lang/AssertionError
+ * dup
+ * ldc #5 // String (don't have this line if no assert message given)
+ * invokespecial #6 // Method java/lang/AssertionError.
+ * athrow
+ * Replace athrow with:
+ * invokestatic #7 // Method org/chromium/base/JavaExceptionReporter.assertFailureHandler
+ * goto 118
+ * JavaExceptionReporter.assertFailureHandler is a function that handles the AssertionError,
+ * 118 is the instruction to execute as if assertion if not enabled.
+ */
+class AssertionEnablerClassAdapter extends ClassVisitor {
+ AssertionEnablerClassAdapter(ClassVisitor visitor) {
+ super(Opcodes.ASM6, visitor);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name, String desc,
+ String signature, String[] exceptions) {
+ return new RewriteAssertMethodVisitor(
+ Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions));
+ }
+
+ static class RewriteAssertMethodVisitor extends MethodVisitor {
+ static final String ASSERTION_DISABLED_NAME = "$assertionsDisabled";
+ static final String INSERT_INSTRUCTION_NAME = "assertFailureHandler";
+ static final String INSERT_INSTRUCTION_DESC =
+ Type.getMethodDescriptor(Type.VOID_TYPE, Type.getObjectType("java/lang/AssertionError"));
+ static final boolean INSERT_INSTRUCTION_ITF = false;
+
+ boolean mStartLoadingAssert;
+ Label mGotoLabel;
+
+ public RewriteAssertMethodVisitor(int api, MethodVisitor mv) {
+ super(api, mv);
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ if (opcode == Opcodes.PUTSTATIC && name.equals(ASSERTION_DISABLED_NAME)) {
+ super.visitInsn(Opcodes.POP); // enable assert
+ } else if (opcode == Opcodes.GETSTATIC && name.equals(ASSERTION_DISABLED_NAME)) {
+ mStartLoadingAssert = true;
+ super.visitFieldInsn(opcode, owner, name, desc);
+ } else {
+ super.visitFieldInsn(opcode, owner, name, desc);
+ }
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ if (mStartLoadingAssert && opcode == Opcodes.IFNE && mGotoLabel == null) {
+ mGotoLabel = label;
+ }
+ super.visitJumpInsn(opcode, label);
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ if (!mStartLoadingAssert || opcode != Opcodes.ATHROW) {
+ super.visitInsn(opcode);
+ } else {
+ super.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ ChromuimAssertionHookMock.class.getCanonicalName().replace('.', '/'),
+ INSERT_INSTRUCTION_NAME,
+ INSERT_INSTRUCTION_DESC,
+ INSERT_INSTRUCTION_ITF);
+ super.visitJumpInsn(Opcodes.GOTO, mGotoLabel);
+ mStartLoadingAssert = false;
+ mGotoLabel = null;
+ }
+ }
+ }
+}
public class RemoveAssertionsTest extends TestBase {
@@ -38,4 +160,75 @@
clazz.method(new MethodSignature(Constants.CLASS_INITIALIZER_NAME, "void", new String[]{}));
assertTrue(!clinit.isPresent());
}
+
+ private Path buildTestToCf(Consumer<InternalOptions> consumer) throws Exception {
+ Path outputJar = temp.getRoot().toPath().resolve("output.jar");
+ R8Command command =
+ ToolHelper.prepareR8CommandBuilder(readClasses(ClassWithAssertions.class))
+ .setMode(CompilationMode.DEBUG)
+ .setOutput(outputJar, OutputMode.ClassFile)
+ .build();
+ ToolHelper.runR8(command, consumer);
+ return outputJar;
+ }
+
+ @Test
+ public void testCfOutput() throws Exception {
+ String main = ClassWithAssertions.class.getCanonicalName();
+ ProcessResult result;
+ // Assertion is hit.
+ result = ToolHelper.runJava(buildTestToCf(options -> {}), "-ea", main, "0");
+ assertEquals(1, result.exitCode);
+ assertEquals("1\n".replace("\n", System.lineSeparator()), result.stdout);
+ // Assertion is not hit.
+ result = ToolHelper.runJava(buildTestToCf(options -> {}), "-ea", main, "1");
+ assertEquals(0, result.exitCode);
+ assertEquals("1\n2\n".replace("\n", System.lineSeparator()), result.stdout);
+ // Assertion is hit, but removed.
+ result = ToolHelper.runJava(
+ buildTestToCf(
+ options -> options.disableAssertions = true), "-ea", main, "0");
+ assertEquals(0, result.exitCode);
+ assertEquals("1\n2\n".replace("\n", System.lineSeparator()), result.stdout);
+ }
+
+ private byte[] identity(byte[] classBytes) {
+ return classBytes;
+ }
+
+ private byte[] chromiumAssertionEnabler(byte[] classBytes) {
+ ClassWriter writer = new ClassWriter(0);
+ new ClassReader(classBytes).accept(new AssertionEnablerClassAdapter(writer), 0);
+ return writer.toByteArray();
+ }
+
+ private AndroidApp runRegress110887293(Function<byte[], byte[]> rewriter) throws Exception {
+ return ToolHelper.runR8(
+ R8Command.builder()
+ .addClassProgramData(
+ rewriter.apply(ToolHelper.getClassAsBytes(ClassWithAssertions.class)),
+ Origin.unknown())
+ .addClassProgramData(
+ ToolHelper.getClassAsBytes(ChromuimAssertionHookMock.class), Origin.unknown())
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .setMode(CompilationMode.DEBUG)
+ .build());
+ }
+
+ @Test
+ public void regress110887293() throws Exception {
+ AndroidApp app;
+ // Assertions removed for default assertion code.
+ app = runRegress110887293(this::identity);
+ assertEquals("1\n2\n", runOnArt(app, ClassWithAssertions.class.getCanonicalName(), "0"));
+ assertEquals("1\n2\n", runOnArt(app, ClassWithAssertions.class.getCanonicalName(), "1"));
+ // Assertions not removed when default assertion code is not present.
+ app = runRegress110887293(this::chromiumAssertionEnabler);
+ assertEquals(
+ "1\nGot AssertionError java.lang.AssertionError\n2\n".replace("\n", System.lineSeparator()),
+ runOnArt(app, ClassWithAssertions.class.getCanonicalName(), "0"));
+ assertEquals(
+ "1\n2\n".replace("\n", System.lineSeparator()),
+ runOnArt(app, ClassWithAssertions.class.getCanonicalName(), "1"));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
index e0d9ecf..f92b1dc 100644
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
@@ -9,12 +9,12 @@
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
+import com.android.tools.r8.utils.dexinspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
index a50a667..c3037f8 100644
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
@@ -11,11 +11,11 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
+import com.android.tools.r8.utils.dexinspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
index 087b5f8..5fe2abd 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
@@ -25,9 +25,9 @@
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import org.junit.Test;
public class StaticValuesTest extends SmaliTestBase {
diff --git a/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingJarTest.java b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingJarTest.java
index ae88c12..7ea9cdd 100644
--- a/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingJarTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingJarTest.java
@@ -18,9 +18,9 @@
import com.android.tools.r8.jasmin.JasminTestBase;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.stream.Collectors;
diff --git a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
index 38247bd..7eae052 100644
--- a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Paths;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/shaking/ClassKindTest.java b/src/test/java/com/android/tools/r8/shaking/ClassKindTest.java
index 3475871..9242842 100644
--- a/src/test/java/com/android/tools/r8/shaking/ClassKindTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ClassKindTest.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.shaking.classkinds.Enum;
import com.android.tools.r8.shaking.classkinds.Interface;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
index 8f00f8d..5fa8edc 100644
--- a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -17,8 +17,8 @@
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
index 6ecdb09..c6ccc61 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.shaking;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -31,7 +31,7 @@
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharSource;
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 1379b95..22b6790 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -27,11 +27,11 @@
import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
-import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
import com.android.tools.r8.utils.KeepingDiagnosticHandler;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index b6a1ac6..24d4ba5 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -14,13 +14,13 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FoundFieldSubject;
-import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.TestDescriptionWatcher;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FoundFieldSubject;
+import com.android.tools.r8.utils.dexinspector.FoundMethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Files;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
index 46feb00..d739348 100644
--- a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
@@ -15,9 +15,9 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking11Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking11Test.java
index 54bec20..f7889ee 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking11Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking11Test.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java
index 7bbde47..d58d975 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking13Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking13Test.java
index 4729897..6cfcd87 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking13Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking13Test.java
@@ -5,11 +5,11 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldAccessInstructionSubject;
-import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldAccessInstructionSubject;
+import com.android.tools.r8.utils.dexinspector.InstructionSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking14Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking14Test.java
index 6891708..8ea939a 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking14Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking14Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking15Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking15Test.java
index 98a7a28..1651fca 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking15Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking15Test.java
@@ -5,10 +5,10 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking17Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking17Test.java
index be0f529..ba01d2f 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking17Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking17Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
index 97d9bfc..26ea045 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking19Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking19Test.java
index e5be477..0c662bb 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking19Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking19Test.java
@@ -3,15 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.examples;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking1Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking1Test.java
index ad2aea7..6b90ec9 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking1Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking1Test.java
@@ -6,8 +6,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking2Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking2Test.java
index 39dd4ab..4c2cfe1 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking2Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking2Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking3Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking3Test.java
index 0aa431a..3996622 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking3Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking3Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking5Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking5Test.java
index f7b0b47..ad13070 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking5Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking5Test.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking6Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking6Test.java
index f93ccd2..9fb2029 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking6Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking6Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking7Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking7Test.java
index 7762040..b44f3aa 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking7Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking7Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking8Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking8Test.java
index afa3003..02cda2a 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking8Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking8Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java
index e3c8b8a..18a1141 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAnnotationremovalTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAnnotationremovalTest.java
index a235df5..dfe1806 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAnnotationremovalTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAnnotationremovalTest.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingEnumprotoTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingEnumprotoTest.java
index 7570990..e544edf 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingEnumprotoTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingEnumprotoTest.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingNestedproto1Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingNestedproto1Test.java
index 2ff5dfc..6650860 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingNestedproto1Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingNestedproto1Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingNestedproto2Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingNestedproto2Test.java
index cb61962..d7a8af5 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingNestedproto2Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingNestedproto2Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingOneofprotoTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingOneofprotoTest.java
index 44f5f89..18f4648 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingOneofprotoTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingOneofprotoTest.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingRepeatedprotoTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingRepeatedprotoTest.java
index bd27c7f..e55d822 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingRepeatedprotoTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingRepeatedprotoTest.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingSimpleproto1Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingSimpleproto1Test.java
index 8c3f4a4..2b49803 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingSimpleproto1Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingSimpleproto1Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingSimpleproto2Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingSimpleproto2Test.java
index 30a9131..ce9659b 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingSimpleproto2Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingSimpleproto2Test.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 642045c..31c97ca 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -36,12 +36,12 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
index e3bffc6..f76d402 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.forceproguardcompatibility;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
@@ -17,10 +17,10 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ImplicitlyKeptDefaultConstructorTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ImplicitlyKeptDefaultConstructorTest.java
index d8615e0..d752e39 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ImplicitlyKeptDefaultConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ImplicitlyKeptDefaultConstructorTest.java
@@ -4,16 +4,16 @@
package com.android.tools.r8.shaking.forceproguardcompatibility.defaultctor;
-import static com.android.tools.r8.utils.DexInspectorMatchers.hasDefaultConstructor;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.hasDefaultConstructor;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
import com.android.tools.r8.smali.ConstantFoldingTest.TriConsumer;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
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 65d1763..e64983d 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
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.ifrule;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
@@ -11,9 +11,9 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java
index 4d0281f..ac92146 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAnnotationTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.shaking.ifrule;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java
index 76c1023..1f5ba35 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java
@@ -3,18 +3,18 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.ifrule;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isNotRenamed;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isRenamed;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isNotRenamed;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTest.java
index 7480e2e..0292344 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnFieldTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.shaking.ifrule;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java b/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
index 22f9ef6..cd2ce6a 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
@@ -4,15 +4,15 @@
package com.android.tools.r8.shaking.ifrule.inlining;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
index b484d73..aed9596 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
@@ -4,16 +4,16 @@
package com.android.tools.r8.shaking.ifrule.verticalclassmerging;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index cae3b06..c841996 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.ArrayList;
diff --git a/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepClassMembersTest.java b/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepClassMembersTest.java
index 12bcdfd..b1ae8bb 100644
--- a/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepClassMembersTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepClassMembersTest.java
@@ -4,18 +4,18 @@
package com.android.tools.r8.shaking.keepclassmembers;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isAbstract;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isAbstract;
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.FieldSubject;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
index 8e39b67..8c71ff7 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -23,7 +23,7 @@
import com.android.tools.r8.shaking.proxy.testclasses.SubInterface;
import com.android.tools.r8.shaking.proxy.testclasses.TestClass;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
index e4be101..365d039 100644
--- a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
@@ -7,17 +7,18 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.testrules;
-import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
-import static org.junit.Assert.assertThat;
+
+import static com.android.tools.r8.utils.dexinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
index 3381473..5f9a6fd 100644
--- a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
@@ -20,8 +20,8 @@
import com.android.tools.r8.ir.code.SingleConstant;
import com.android.tools.r8.ir.code.WideConstant;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 371c4d1..41e894e 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -32,11 +32,11 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
index 5270cd4..c8713e3 100644
--- a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
@@ -12,8 +12,8 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
index b6db233..7287cc3 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
@@ -9,8 +9,8 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 6c4b884..7aabadb 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -23,10 +23,10 @@
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
diff --git a/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java b/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java
index 406c63f..51c6ca5 100644
--- a/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java
+++ b/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java
@@ -4,8 +4,9 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.dexinspector.ClassSubject;
+import com.android.tools.r8.utils.dexinspector.DexInspector;
+import com.android.tools.r8.utils.dexinspector.MethodSubject;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
deleted file mode 100644
index eba3be8..0000000
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ /dev/null
@@ -1,1664 +0,0 @@
-// Copyright (c) 2016, 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 static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.StringResource;
-import com.android.tools.r8.code.Const4;
-import com.android.tools.r8.code.ConstString;
-import com.android.tools.r8.code.Goto;
-import com.android.tools.r8.code.IfEqz;
-import com.android.tools.r8.code.IfNez;
-import com.android.tools.r8.code.Iget;
-import com.android.tools.r8.code.IgetBoolean;
-import com.android.tools.r8.code.IgetByte;
-import com.android.tools.r8.code.IgetChar;
-import com.android.tools.r8.code.IgetObject;
-import com.android.tools.r8.code.IgetShort;
-import com.android.tools.r8.code.IgetWide;
-import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.InvokeDirect;
-import com.android.tools.r8.code.InvokeDirectRange;
-import com.android.tools.r8.code.InvokeInterface;
-import com.android.tools.r8.code.InvokeInterfaceRange;
-import com.android.tools.r8.code.InvokeStatic;
-import com.android.tools.r8.code.InvokeStaticRange;
-import com.android.tools.r8.code.InvokeSuper;
-import com.android.tools.r8.code.InvokeSuperRange;
-import com.android.tools.r8.code.InvokeVirtual;
-import com.android.tools.r8.code.InvokeVirtualRange;
-import com.android.tools.r8.code.Iput;
-import com.android.tools.r8.code.IputBoolean;
-import com.android.tools.r8.code.IputByte;
-import com.android.tools.r8.code.IputChar;
-import com.android.tools.r8.code.IputObject;
-import com.android.tools.r8.code.IputShort;
-import com.android.tools.r8.code.IputWide;
-import com.android.tools.r8.code.Nop;
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.code.Sget;
-import com.android.tools.r8.code.SgetBoolean;
-import com.android.tools.r8.code.SgetByte;
-import com.android.tools.r8.code.SgetChar;
-import com.android.tools.r8.code.SgetObject;
-import com.android.tools.r8.code.SgetShort;
-import com.android.tools.r8.code.SgetWide;
-import com.android.tools.r8.code.Sput;
-import com.android.tools.r8.code.SputBoolean;
-import com.android.tools.r8.code.SputByte;
-import com.android.tools.r8.code.SputChar;
-import com.android.tools.r8.code.SputObject;
-import com.android.tools.r8.code.SputShort;
-import com.android.tools.r8.code.SputWide;
-import com.android.tools.r8.code.Throw;
-import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationElement;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexEncodedAnnotation;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.DexValue.DexValueArray;
-import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNamingForNameMapper;
-import com.android.tools.r8.naming.MemberNaming;
-import com.android.tools.r8.naming.MemberNaming.FieldSignature;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.naming.signature.GenericSignatureAction;
-import com.android.tools.r8.naming.signature.GenericSignatureParser;
-import com.android.tools.r8.smali.SmaliBuilder;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.concurrent.ExecutionException;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-public class DexInspector {
-
- private final DexApplication application;
- private final DexItemFactory dexItemFactory;
- private final ClassNameMapper mapping;
- private final BiMap<String, String> originalToObfuscatedMapping;
-
- private final InstructionSubjectFactory factory = new InstructionSubjectFactory();
-
- public static MethodSignature MAIN =
- new MethodSignature("main", "void", new String[]{"java.lang.String[]"});
-
- public DexInspector(Path file, String mappingFile) throws IOException, ExecutionException {
- this(Collections.singletonList(file), mappingFile);
- }
-
- public DexInspector(Path file) throws IOException, ExecutionException {
- this(Collections.singletonList(file), null);
- }
-
- public DexInspector(List<Path> files) throws IOException, ExecutionException {
- this(files, null);
- }
-
- public DexInspector(List<Path> files, String mappingFile)
- throws IOException, ExecutionException {
- if (mappingFile != null) {
- this.mapping = ClassNameMapper.mapperFromFile(Paths.get(mappingFile));
- originalToObfuscatedMapping = this.mapping.getObfuscatedToOriginalMapping().inverse();
- } else {
- this.mapping = null;
- originalToObfuscatedMapping = null;
- }
- Timing timing = new Timing("DexInspector");
- InternalOptions options = new InternalOptions();
- dexItemFactory = options.itemFactory;
- AndroidApp input = AndroidApp.builder().addProgramFiles(files).build();
- application = new ApplicationReader(input, options, timing).read();
- }
-
- public DexInspector(AndroidApp app) throws IOException, ExecutionException {
- this(
- new ApplicationReader(app, new InternalOptions(), new Timing("DexInspector"))
- .read(app.getProguardMapOutputData()));
- }
-
- public DexInspector(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
- throws IOException, ExecutionException {
- this(
- new ApplicationReader(app, runOptionsConsumer(optionsConsumer), new Timing("DexInspector"))
- .read(app.getProguardMapOutputData()));
- }
-
- private static InternalOptions runOptionsConsumer(Consumer<InternalOptions> optionsConsumer) {
- InternalOptions internalOptions = new InternalOptions();
- optionsConsumer.accept(internalOptions);
- return internalOptions;
- }
-
- public DexInspector(AndroidApp app, Path proguardMap) throws IOException, ExecutionException {
- this(
- new ApplicationReader(app, new InternalOptions(), new Timing("DexInspector"))
- .read(StringResource.fromFile(proguardMap)));
- }
-
- public DexInspector(DexApplication application) {
- dexItemFactory = application.dexItemFactory;
- this.application = application;
- this.mapping = application.getProguardMap();
- originalToObfuscatedMapping =
- mapping == null ? null : mapping.getObfuscatedToOriginalMapping().inverse();
- }
-
- public DexItemFactory getFactory() {
- return dexItemFactory;
- }
-
- private DexType toDexType(String string) {
- return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(string));
- }
-
- private DexType toDexTypeIgnorePrimitives(String string) {
- return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptorIgnorePrimitives(string));
- }
-
- private static <S, T extends Subject> void forAll(S[] items,
- BiFunction<S, FoundClassSubject, ? extends T> constructor,
- FoundClassSubject clazz,
- Consumer<T> consumer) {
- for (S item : items) {
- consumer.accept(constructor.apply(item, clazz));
- }
- }
-
- private static <S, T extends Subject> void forAll(Iterable<S> items, Function<S, T> constructor,
- Consumer<T> consumer) {
- for (S item : items) {
- consumer.accept(constructor.apply(item));
- }
- }
-
- DexAnnotation findAnnotation(String name, DexAnnotationSet annotations) {
- for (DexAnnotation annotation : annotations.annotations) {
- DexType type = annotation.annotation.type;
- String original = mapping == null ? type.toSourceString() : mapping.originalNameOf(type);
- if (original.equals(name)) {
- return annotation;
- }
- }
- return null;
- }
-
- public String getFinalSignatureAttribute(DexAnnotationSet annotations) {
- DexAnnotation annotation =
- findAnnotation("dalvik.annotation.Signature", annotations);
- if (annotation == null) {
- return null;
- }
- assert annotation.annotation.elements.length == 1;
- DexAnnotationElement element = annotation.annotation.elements[0];
- assert element.value instanceof DexValueArray;
- StringBuilder builder = new StringBuilder();
- DexValueArray valueArray = (DexValueArray) element.value;
- for (DexValue value : valueArray.getValues()) {
- assertTrue(value instanceof DexValueString);
- DexValueString s = (DexValueString) value;
- builder.append(s.getValue());
- }
- return builder.toString();
- }
-
- public String getOriginalSignatureAttribute(
- DexAnnotationSet annotations, BiConsumer<GenericSignatureParser, String> parse) {
- String finalSignature = getFinalSignatureAttribute(annotations);
- if (finalSignature == null || mapping == null) {
- return finalSignature;
- }
-
- GenericSignatureGenerater rewriter = new GenericSignatureGenerater();
- GenericSignatureParser<String> parser = new GenericSignatureParser<>(rewriter);
- parse.accept(parser, finalSignature);
- return rewriter.getSignature();
- }
-
-
- public ClassSubject clazz(Class clazz) {
- return clazz(clazz.getTypeName());
- }
-
- /**
- * Lookup a class by name. This allows both original and obfuscated names.
- */
- public ClassSubject clazz(String name) {
- ClassNamingForNameMapper naming = null;
- if (mapping != null) {
- String obfuscated = originalToObfuscatedMapping.get(name);
- if (obfuscated != null) {
- naming = mapping.getClassNaming(obfuscated);
- name = obfuscated;
- } else {
- // Figure out if the name is an already obfuscated name.
- String original = originalToObfuscatedMapping.inverse().get(name);
- if (original != null) {
- naming = mapping.getClassNaming(name);
- }
- }
- }
- DexClass clazz = application.definitionFor(toDexTypeIgnorePrimitives(name));
- if (clazz == null) {
- return new AbsentClassSubject();
- }
- return new FoundClassSubject(clazz, naming);
- }
-
- public void forAllClasses(Consumer<FoundClassSubject> inspection) {
- forAll(application.classes(), cls -> {
- ClassSubject subject = clazz(cls.type.toSourceString());
- assert subject.isPresent();
- return (FoundClassSubject) subject;
- }, inspection);
- }
-
- public List<FoundClassSubject> allClasses() {
- ImmutableList.Builder<FoundClassSubject> builder = ImmutableList.builder();
- forAllClasses(builder::add);
- return builder.build();
- }
-
- public MethodSubject method(Method method) {
- ClassSubject clazz = clazz(method.getDeclaringClass());
- if (!clazz.isPresent()) {
- return new AbsentMethodSubject();
- }
- return clazz.method(method);
- }
-
- private String getObfuscatedTypeName(String originalTypeName) {
- String obfuscatedType = null;
- if (mapping != null) {
- obfuscatedType = originalToObfuscatedMapping.get(originalTypeName);
- }
- obfuscatedType = obfuscatedType == null ? originalTypeName : obfuscatedType;
- return obfuscatedType;
- }
-
- public abstract class Subject {
-
- public abstract boolean isPresent();
- public abstract boolean isRenamed();
- }
-
- public abstract class AnnotationSubject extends Subject {
-
- public abstract DexEncodedAnnotation getAnnotation();
- }
-
- public class FoundAnnotationSubject extends AnnotationSubject {
-
- private final DexAnnotation annotation;
-
- private FoundAnnotationSubject(DexAnnotation annotation) {
- this.annotation = annotation;
- }
-
- @Override
- public boolean isPresent() {
- return true;
- }
-
- @Override
- public boolean isRenamed() {
- return false;
- }
-
- @Override
- public DexEncodedAnnotation getAnnotation() {
- return annotation.annotation;
- }
- }
-
- public class AbsentAnnotationSubject extends AnnotationSubject {
-
- @Override
- public boolean isPresent() {
- return false;
- }
-
- @Override
- public boolean isRenamed() {
- return false;
- }
-
- @Override
- public DexEncodedAnnotation getAnnotation() {
- throw new UnsupportedOperationException();
- }
- }
-
-
- public abstract class ClassSubject extends Subject {
-
- public abstract void forAllMethods(Consumer<FoundMethodSubject> inspection);
-
- public MethodSubject method(Method method) {
- List<String> parameters = new ArrayList<>();
- for (Class<?> parameterType : method.getParameterTypes()) {
- parameters.add(parameterType.getTypeName());
- }
- return method(method.getReturnType().getTypeName(), method.getName(), parameters);
- }
-
- public abstract MethodSubject method(String returnType, String name, List<String> parameters);
-
- public MethodSubject clinit() {
- return method("void", "<clinit>", ImmutableList.of());
- }
-
- public MethodSubject init(List<String> parameters) {
- return method("void", "<init>", parameters);
- }
-
- public MethodSubject method(MethodSignature signature) {
- return method(signature.type, signature.name, ImmutableList.copyOf(signature.parameters));
- }
-
- public MethodSubject method(SmaliBuilder.MethodSignature signature) {
- return method(
- signature.returnType, signature.name, ImmutableList.copyOf(signature.parameterTypes));
- }
-
- public abstract void forAllFields(Consumer<FoundFieldSubject> inspection);
-
- public abstract FieldSubject field(String type, String name);
-
- public abstract boolean isAbstract();
-
- public abstract boolean isAnnotation();
-
- public String dumpMethods() {
- StringBuilder dump = new StringBuilder();
- forAllMethods((FoundMethodSubject method) ->
- dump.append(method.getMethod().toString())
- .append(method.getMethod().codeToString()));
- return dump.toString();
- }
-
- public abstract DexClass getDexClass();
-
- public abstract AnnotationSubject annotation(String name);
-
- public abstract String getOriginalName();
-
- public abstract String getOriginalDescriptor();
-
- public abstract String getFinalName();
-
- public abstract String getFinalDescriptor();
-
- public abstract boolean isMemberClass();
-
- public abstract boolean isLocalClass();
-
- public abstract boolean isAnonymousClass();
-
- public abstract String getOriginalSignatureAttribute();
-
- public abstract String getFinalSignatureAttribute();
- }
-
- private class AbsentClassSubject extends ClassSubject {
-
- @Override
- public boolean isPresent() {
- return false;
- }
-
- @Override
- public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
- }
-
- @Override
- public MethodSubject method(String returnType, String name, List<String> parameters) {
- return new AbsentMethodSubject();
- }
-
- @Override
- public void forAllFields(Consumer<FoundFieldSubject> inspection) {
- }
-
- @Override
- public FieldSubject field(String type, String name) {
- return new AbsentFieldSubject();
- }
-
- @Override
- public boolean isAbstract() {
- return false;
- }
-
- @Override
- public boolean isAnnotation() {
- return false;
- }
-
- @Override
- public DexClass getDexClass() {
- return null;
- }
-
- @Override
- public AnnotationSubject annotation(String name) {
- return new AbsentAnnotationSubject();
- }
-
- @Override
- public String getOriginalName() {
- return null;
- }
-
- @Override
- public String getOriginalDescriptor() {
- return null;
- }
-
- @Override
- public String getFinalName() {
- return null;
- }
-
- @Override
- public String getFinalDescriptor() {
- return null;
- }
-
- @Override
- public boolean isRenamed() {
- return false;
- }
-
- @Override
- public boolean isMemberClass() {
- return false;
- }
-
- @Override
- public boolean isLocalClass() {
- return false;
- }
-
- @Override
- public boolean isAnonymousClass() {
- return false;
- }
-
- @Override
- public String getOriginalSignatureAttribute() {
- return null;
- }
-
- @Override
- public String getFinalSignatureAttribute() {
- return null;
- }
- }
-
- public class FoundClassSubject extends ClassSubject {
-
- private final DexClass dexClass;
- private final ClassNamingForNameMapper naming;
-
- private FoundClassSubject(DexClass dexClass, ClassNamingForNameMapper naming) {
- this.dexClass = dexClass;
- this.naming = naming;
- }
-
- @Override
- public boolean isPresent() {
- return true;
- }
-
- @Override
- public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
- forAll(dexClass.directMethods(), FoundMethodSubject::new, this, inspection);
- forAll(dexClass.virtualMethods(), FoundMethodSubject::new, this, inspection);
- }
-
- @Override
- public MethodSubject method(String returnType, String name, List<String> parameters) {
- DexType[] parameterTypes = new DexType[parameters.size()];
- for (int i = 0; i < parameters.size(); i++) {
- parameterTypes[i] = toDexType(getObfuscatedTypeName(parameters.get(i)));
- }
- DexProto proto = dexItemFactory.createProto(toDexType(getObfuscatedTypeName(returnType)),
- parameterTypes);
- if (naming != null) {
- String[] parameterStrings = new String[parameterTypes.length];
- Signature signature = new MethodSignature(name, returnType,
- parameters.toArray(parameterStrings));
- MemberNaming methodNaming = naming.lookupByOriginalSignature(signature);
- if (methodNaming != null) {
- name = methodNaming.getRenamedName();
- }
- }
- DexMethod dexMethod =
- dexItemFactory.createMethod(dexClass.type, proto, dexItemFactory.createString(name));
- DexEncodedMethod encoded = findMethod(dexClass.directMethods(), dexMethod);
- if (encoded == null) {
- encoded = findMethod(dexClass.virtualMethods(), dexMethod);
- }
- return encoded == null ? new AbsentMethodSubject() : new FoundMethodSubject(encoded, this);
- }
-
- private DexEncodedMethod findMethod(DexEncodedMethod[] methods, DexMethod dexMethod) {
- for (DexEncodedMethod method : methods) {
- if (method.method.equals(dexMethod)) {
- return method;
- }
- }
- return null;
- }
-
- @Override
- public void forAllFields(Consumer<FoundFieldSubject> inspection) {
- forAll(dexClass.staticFields(), FoundFieldSubject::new, this, inspection);
- forAll(dexClass.instanceFields(), FoundFieldSubject::new, this, inspection);
- }
-
- @Override
- public FieldSubject field(String type, String name) {
- String obfuscatedType = getObfuscatedTypeName(type);
- MemberNaming fieldNaming = null;
- if (naming != null) {
- fieldNaming = naming.lookupByOriginalSignature(
- new FieldSignature(name, type));
- }
- String obfuscatedName = fieldNaming == null ? name : fieldNaming.getRenamedName();
-
- DexField field = dexItemFactory.createField(dexClass.type,
- toDexType(obfuscatedType), dexItemFactory.createString(obfuscatedName));
- DexEncodedField encoded = findField(dexClass.staticFields(), field);
- if (encoded == null) {
- encoded = findField(dexClass.instanceFields(), field);
- }
- return encoded == null ? new AbsentFieldSubject() : new FoundFieldSubject(encoded, this);
- }
-
- @Override
- public boolean isAbstract() {
- return dexClass.accessFlags.isAbstract();
- }
-
- @Override
- public boolean isAnnotation() {
- return dexClass.accessFlags.isAnnotation();
- }
-
- private DexEncodedField findField(DexEncodedField[] fields, DexField dexField) {
- for (DexEncodedField field : fields) {
- if (field.field.equals(dexField)) {
- return field;
- }
- }
- return null;
- }
-
- @Override
- public DexClass getDexClass() {
- return dexClass;
- }
-
- @Override
- public AnnotationSubject annotation(String name) {
- // Ensure we don't check for annotations represented as attributes.
- assert !name.endsWith("EnclosingClass")
- && !name.endsWith("EnclosingMethod")
- && !name.endsWith("InnerClass");
- DexAnnotation annotation = findAnnotation(name, dexClass.annotations);
- return annotation == null
- ? new AbsentAnnotationSubject()
- : new FoundAnnotationSubject(annotation);
- }
-
- @Override
- public String getOriginalName() {
- if (naming != null) {
- return naming.originalName;
- } else {
- return getFinalName();
- }
- }
-
- @Override
- public String getOriginalDescriptor() {
- if (naming != null) {
- return DescriptorUtils.javaTypeToDescriptor(naming.originalName);
- } else {
- return getFinalDescriptor();
- }
- }
-
- @Override
- public String getFinalName() {
- return DescriptorUtils.descriptorToJavaType(getFinalDescriptor());
- }
-
- @Override
- public String getFinalDescriptor() {
- return dexClass.type.descriptor.toString();
- }
-
- @Override
- public boolean isRenamed() {
- return naming != null && !getFinalDescriptor().equals(getOriginalDescriptor());
- }
-
- private InnerClassAttribute getInnerClassAttribute() {
- for (InnerClassAttribute innerClassAttribute : dexClass.getInnerClasses()) {
- if (dexClass.type == innerClassAttribute.getInner()) {
- return innerClassAttribute;
- }
- }
- return null;
- }
-
- @Override
- public boolean isLocalClass() {
- InnerClassAttribute innerClass = getInnerClassAttribute();
- return innerClass != null
- && innerClass.isNamed()
- && dexClass.getEnclosingMethod() != null;
- }
-
- @Override
- public boolean isMemberClass() {
- InnerClassAttribute innerClass = getInnerClassAttribute();
- return innerClass != null
- && innerClass.getOuter() != null
- && innerClass.isNamed()
- && dexClass.getEnclosingMethod() == null;
- }
-
- @Override
- public boolean isAnonymousClass() {
- InnerClassAttribute innerClass = getInnerClassAttribute();
- return innerClass != null
- && innerClass.isAnonymous()
- && dexClass.getEnclosingMethod() != null;
- }
-
- @Override
- public String getOriginalSignatureAttribute() {
- return DexInspector.this.getOriginalSignatureAttribute(
- dexClass.annotations, GenericSignatureParser::parseClassSignature);
- }
-
- @Override
- public String getFinalSignatureAttribute() {
- return DexInspector.this.getFinalSignatureAttribute(dexClass.annotations);
- }
-
- @Override
- public String toString() {
- return dexClass.toSourceString();
- }
- }
-
- public abstract class MemberSubject extends Subject {
-
- public abstract boolean isPublic();
-
- public abstract boolean isStatic();
-
- public abstract boolean isFinal();
-
- public abstract Signature getOriginalSignature();
-
- public abstract Signature getFinalSignature();
-
- public String getOriginalName() {
- Signature originalSignature = getOriginalSignature();
- return originalSignature == null ? null : originalSignature.name;
- }
-
- public String getFinalName() {
- Signature finalSignature = getFinalSignature();
- return finalSignature == null ? null : finalSignature.name;
- }
- }
-
- public abstract class MethodSubject extends MemberSubject {
-
- public abstract boolean isAbstract();
-
- public abstract boolean isBridge();
-
- public abstract boolean isInstanceInitializer();
-
- public abstract boolean isClassInitializer();
-
- public abstract String getOriginalSignatureAttribute();
-
- public abstract String getFinalSignatureAttribute();
-
- public abstract DexEncodedMethod getMethod();
-
- public Iterator<InstructionSubject> iterateInstructions() {
- return null;
- }
-
- public <T extends InstructionSubject> Iterator<T> iterateInstructions(
- Predicate<InstructionSubject> filter) {
- return null;
- }
- }
-
- public class AbsentMethodSubject extends MethodSubject {
-
- @Override
- public boolean isPresent() {
- return false;
- }
-
- @Override
- public boolean isRenamed() {
- return false;
- }
-
- @Override
- public boolean isPublic() {
- return false;
- }
-
- @Override
- public boolean isStatic() {
- return false;
- }
-
- @Override
- public boolean isFinal() {
- return false;
- }
-
- @Override
- public boolean isAbstract() {
- return false;
- }
-
- @Override
- public boolean isBridge() {
- return false;
- }
-
- @Override
- public boolean isInstanceInitializer() {
- return false;
- }
-
- @Override
- public boolean isClassInitializer() {
- return false;
- }
-
- @Override
- public DexEncodedMethod getMethod() {
- return null;
- }
-
- @Override
- public Signature getOriginalSignature() {
- return null;
- }
-
- @Override
- public Signature getFinalSignature() {
- return null;
- }
-
- @Override
- public String getOriginalSignatureAttribute() {
- return null;
- }
-
- @Override
- public String getFinalSignatureAttribute() {
- return null;
- }
- }
-
- public class FoundMethodSubject extends MethodSubject {
-
- private final FoundClassSubject clazz;
- private final DexEncodedMethod dexMethod;
-
- public FoundMethodSubject(DexEncodedMethod encoded, FoundClassSubject clazz) {
- this.clazz = clazz;
- this.dexMethod = encoded;
- }
-
- @Override
- public boolean isPresent() {
- return true;
- }
-
- @Override
- public boolean isRenamed() {
- return clazz.naming != null && !getFinalSignature().name.equals(getOriginalSignature().name);
- }
-
- @Override
- public boolean isPublic() {
- return dexMethod.accessFlags.isPublic();
- }
-
- @Override
- public boolean isStatic() {
- return dexMethod.accessFlags.isStatic();
- }
-
- @Override
- public boolean isFinal() {
- return dexMethod.accessFlags.isFinal();
- }
-
- @Override
- public boolean isAbstract() {
- return dexMethod.accessFlags.isAbstract();
- }
-
- @Override
- public boolean isBridge() {
- return dexMethod.accessFlags.isBridge();
- }
-
- @Override
- public boolean isInstanceInitializer() {
- return dexMethod.isInstanceInitializer();
- }
-
- @Override
- public boolean isClassInitializer() {
- return dexMethod.isClassInitializer();
- }
-
- @Override
- public DexEncodedMethod getMethod() {
- return dexMethod;
- }
-
- @Override
- public MethodSignature getOriginalSignature() {
- MethodSignature signature = getFinalSignature();
- if (clazz.naming == null) {
- return signature;
- }
-
- // Map the parameters and return type to original names. This is needed as the in the
- // Proguard map the names on the left side are the original names. E.g.
- //
- // X -> a
- // X method(X) -> a
- //
- // whereas the final signature is for X.a is "a (a)"
- String[] OriginalParameters = new String[signature.parameters.length];
- for (int i = 0; i < OriginalParameters.length; i++) {
- String obfuscated = signature.parameters[i];
- String original = originalToObfuscatedMapping.inverse().get(obfuscated);
- OriginalParameters[i] = original != null ? original : obfuscated;
- }
- String obfuscatedReturnType = signature.type;
- String originalReturnType = originalToObfuscatedMapping.inverse().get(obfuscatedReturnType);
- String returnType = originalReturnType != null ? originalReturnType : obfuscatedReturnType;
-
- MethodSignature lookupSignature =
- new MethodSignature(signature.name, returnType, OriginalParameters);
-
- MemberNaming memberNaming = clazz.naming.lookup(lookupSignature);
- return memberNaming != null
- ? (MethodSignature) memberNaming.getOriginalSignature()
- : signature;
- }
-
- @Override
- public MethodSignature getFinalSignature() {
- return MemberNaming.MethodSignature.fromDexMethod(dexMethod.method);
- }
-
- @Override
- public String getOriginalSignatureAttribute() {
- return DexInspector.this.getOriginalSignatureAttribute(
- dexMethod.annotations, GenericSignatureParser::parseMethodSignature);
- }
-
- @Override
- public String getFinalSignatureAttribute() {
- return DexInspector.this.getFinalSignatureAttribute(dexMethod.annotations);
- }
-
- @Override
- public Iterator<InstructionSubject> iterateInstructions() {
- return new InstructionIterator(this);
- }
-
- @Override
- public <T extends InstructionSubject> Iterator<T> iterateInstructions(
- Predicate<InstructionSubject> filter) {
- return new FilteredInstructionIterator<>(this, filter);
- }
-
- @Override
- public String toString() {
- return dexMethod.toSourceString();
- }
- }
-
- public abstract class FieldSubject extends MemberSubject {
- public abstract boolean hasExplicitStaticValue();
-
- public abstract DexEncodedField getField();
-
- public abstract DexValue getStaticValue();
-
- public abstract boolean isRenamed();
-
- public abstract String getOriginalSignatureAttribute();
-
- public abstract String getFinalSignatureAttribute();
- }
-
- public class AbsentFieldSubject extends FieldSubject {
-
- @Override
- public boolean isPublic() {
- return false;
- }
-
- @Override
- public boolean isStatic() {
- return false;
- }
-
- @Override
- public boolean isFinal() {
- return false;
- }
-
- @Override
- public boolean isPresent() {
- return false;
- }
-
- @Override
- public boolean isRenamed() {
- return false;
- }
-
- @Override
- public Signature getOriginalSignature() {
- return null;
- }
-
- @Override
- public Signature getFinalSignature() {
- return null;
- }
-
- @Override
- public boolean hasExplicitStaticValue() {
- return false;
- }
-
- @Override
- public DexValue getStaticValue() {
- return null;
- }
-
- @Override
- public DexEncodedField getField() {
- return null;
- }
-
- @Override
- public String getOriginalSignatureAttribute() {
- return null;
- }
-
- @Override
- public String getFinalSignatureAttribute() {
- return null;
- }
- }
-
- public class FoundFieldSubject extends FieldSubject {
-
- private final FoundClassSubject clazz;
- private final DexEncodedField dexField;
-
- public FoundFieldSubject(DexEncodedField dexField, FoundClassSubject clazz) {
- this.clazz = clazz;
- this.dexField = dexField;
- }
-
- @Override
- public boolean isPublic() {
- return dexField.accessFlags.isPublic();
- }
-
- @Override
- public boolean isStatic() {
- return dexField.accessFlags.isStatic();
- }
-
- @Override
- public boolean isFinal() {
- return dexField.accessFlags.isFinal();
- }
-
- @Override
- public boolean isPresent() {
- return true;
- }
-
- @Override
- public boolean isRenamed() {
- return clazz.naming != null && !getFinalSignature().name.equals(getOriginalSignature().name);
- }
-
-
- public TypeSubject type() {
- return new TypeSubject(dexField.field.type);
- }
-
- @Override
- public FieldSignature getOriginalSignature() {
- FieldSignature signature = getFinalSignature();
- if (clazz.naming == null) {
- return signature;
- }
-
- // Map the type to the original name. This is needed as the in the Proguard map the
- // names on the left side are the original names. E.g.
- //
- // X -> a
- // X field -> a
- //
- // whereas the final signature is for X.a is "a a"
- String obfuscatedType = signature.type;
- String originalType = originalToObfuscatedMapping.inverse().get(obfuscatedType);
- String fieldType = originalType != null ? originalType : obfuscatedType;
-
- FieldSignature lookupSignature = new FieldSignature(signature.name, fieldType);
-
- MemberNaming memberNaming = clazz.naming.lookup(lookupSignature);
- return memberNaming != null
- ? (FieldSignature) memberNaming.getOriginalSignature()
- : signature;
- }
-
- @Override
- public FieldSignature getFinalSignature() {
- return MemberNaming.FieldSignature.fromDexField(dexField.field);
- }
-
- @Override
- public boolean hasExplicitStaticValue() {
- return isStatic() && dexField.hasExplicitStaticValue();
- }
-
- @Override
- public DexValue getStaticValue() {
- return dexField.getStaticValue();
- }
-
- @Override
- public DexEncodedField getField() {
- return dexField;
- }
-
- @Override
- public String getOriginalSignatureAttribute() {
- return DexInspector.this.getOriginalSignatureAttribute(
- dexField.annotations, GenericSignatureParser::parseFieldSignature);
- }
-
- @Override
- public String getFinalSignatureAttribute() {
- return DexInspector.this.getFinalSignatureAttribute(dexField.annotations);
- }
-
- @Override
- public String toString() {
- return dexField.toSourceString();
- }
- }
-
- public class TypeSubject extends Subject {
-
- private final DexType dexType;
-
- TypeSubject(DexType dexType) {
- this.dexType = dexType;
- }
-
- @Override
- public boolean isPresent() {
- return true;
- }
-
- @Override
- public boolean isRenamed() {
- return false;
- }
-
- public boolean is(String type) {
- return dexType.equals(toDexType(type));
- }
-
- public String toString() {
- return dexType.toSourceString();
- }
- }
-
- private class InstructionSubjectFactory {
-
- InstructionSubject create(Instruction instruction) {
- if (isInvoke(instruction)) {
- return new InvokeInstructionSubject(this, instruction);
- } else if (isFieldAccess(instruction)) {
- return new FieldAccessInstructionSubject(this, instruction);
- } else {
- return new InstructionSubject(this, instruction);
- }
- }
-
- boolean isInvoke(Instruction instruction) {
- return isInvokeVirtual(instruction)
- || isInvokeInterface(instruction)
- || isInvokeDirect(instruction)
- || isInvokeSuper(instruction)
- || isInvokeStatic(instruction);
- }
-
- boolean isInvokeVirtual(Instruction instruction) {
- return instruction instanceof InvokeVirtual || instruction instanceof InvokeVirtualRange;
- }
-
- boolean isInvokeInterface(Instruction instruction) {
- return instruction instanceof InvokeInterface || instruction instanceof InvokeInterfaceRange;
- }
-
- boolean isInvokeDirect(Instruction instruction) {
- return instruction instanceof InvokeDirect || instruction instanceof InvokeDirectRange;
- }
-
- boolean isInvokeSuper(Instruction instruction) {
- return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange;
- }
-
- boolean isInvokeStatic(Instruction instruction) {
- return instruction instanceof InvokeStatic || instruction instanceof InvokeStaticRange;
- }
-
- boolean isNop(Instruction instruction) {
- return instruction instanceof Nop;
- }
-
- boolean isGoto(Instruction instruction) {
- return instruction instanceof Goto;
- }
-
- boolean isReturnVoid(Instruction instruction) {
- return instruction instanceof ReturnVoid;
- }
-
- boolean isConst4(Instruction instruction) {
- return instruction instanceof Const4;
- }
-
- boolean isThrow(Instruction instruction) {
- return instruction instanceof Throw;
- }
-
- boolean isConstString(Instruction instruction) {
- return instruction instanceof ConstString;
- }
-
- boolean isConstString(Instruction instruction, String value) {
- return instruction instanceof ConstString
- && ((ConstString) instruction).BBBB.toSourceString().equals(value);
- }
-
- boolean isIfNez(Instruction instruction) {
- return instruction instanceof IfNez;
- }
-
- boolean isIfEqz(Instruction instruction) {
- return instruction instanceof IfEqz;
- }
-
- boolean isFieldAccess(Instruction instruction) {
- return isInstanceGet(instruction)
- || isInstancePut(instruction)
- || isStaticGet(instruction)
- || isStaticSet(instruction);
- }
-
- boolean isInstanceGet(Instruction instruction) {
- return instruction instanceof Iget
- || instruction instanceof IgetBoolean
- || instruction instanceof IgetByte
- || instruction instanceof IgetShort
- || instruction instanceof IgetChar
- || instruction instanceof IgetWide
- || instruction instanceof IgetObject;
- }
-
- boolean isInstancePut(Instruction instruction) {
- return instruction instanceof Iput
- || instruction instanceof IputBoolean
- || instruction instanceof IputByte
- || instruction instanceof IputShort
- || instruction instanceof IputChar
- || instruction instanceof IputWide
- || instruction instanceof IputObject;
- }
-
- boolean isStaticGet(Instruction instruction) {
- return instruction instanceof Sget
- || instruction instanceof SgetBoolean
- || instruction instanceof SgetByte
- || instruction instanceof SgetShort
- || instruction instanceof SgetChar
- || instruction instanceof SgetWide
- || instruction instanceof SgetObject;
- }
-
- boolean isStaticSet(Instruction instruction) {
- return instruction instanceof Sput
- || instruction instanceof SputBoolean
- || instruction instanceof SputByte
- || instruction instanceof SputShort
- || instruction instanceof SputChar
- || instruction instanceof SputWide
- || instruction instanceof SputObject;
- }
- }
-
- public class InstructionSubject {
-
- protected final InstructionSubjectFactory factory;
- protected final Instruction instruction;
-
- protected InstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
- this.factory = factory;
- this.instruction = instruction;
- }
-
- public boolean isInvoke() {
- return factory.isInvoke(instruction);
- }
-
- public boolean isFieldAccess() {
- return factory.isFieldAccess(instruction);
- }
-
- public boolean isInvokeVirtual() {
- return factory.isInvokeVirtual(instruction);
- }
-
- public boolean isInvokeInterface() {
- return factory.isInvokeInterface(instruction);
- }
-
- public boolean isInvokeDirect() {
- return factory.isInvokeDirect(instruction);
- }
-
- public boolean isInvokeSuper() {
- return factory.isInvokeSuper(instruction);
- }
-
- public boolean isInvokeStatic() {
- return factory.isInvokeStatic(instruction);
- }
-
- boolean isFieldAccess(Instruction instruction) {
- return factory.isFieldAccess(instruction);
- }
-
- public boolean isNop() {
- return factory.isNop(instruction);
- }
-
- public boolean isConstString() {
- return factory.isConstString(instruction);
- }
-
- public boolean isConstString(String value) {
- return factory.isConstString(instruction, value);
- }
-
- public boolean isGoto() {
- return factory.isGoto(instruction);
- }
-
- public boolean isIfNez() {
- return factory.isIfNez(instruction);
- }
-
- public boolean isIfEqz() {
- return factory.isIfEqz(instruction);
- }
-
- public boolean isReturnVoid() {
- return factory.isReturnVoid(instruction);
- }
-
- public boolean isConst4() {
- return factory.isConst4(instruction);
- }
-
- public boolean isThrow() {
- return factory.isThrow(instruction);
- }
- }
-
- public class InvokeInstructionSubject extends InstructionSubject {
-
- InvokeInstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
- super(factory, instruction);
- assert isInvoke();
- }
-
- public TypeSubject holder() {
- return new TypeSubject(invokedMethod().getHolder());
- }
-
- public DexMethod invokedMethod() {
- if (instruction instanceof InvokeVirtual) {
- return ((InvokeVirtual) instruction).getMethod();
- }
- if (instruction instanceof InvokeVirtualRange) {
- return ((InvokeVirtualRange) instruction).getMethod();
- }
- if (instruction instanceof InvokeInterface) {
- return ((InvokeInterface) instruction).getMethod();
- }
- if (instruction instanceof InvokeInterfaceRange) {
- return ((InvokeInterfaceRange) instruction).getMethod();
- }
- if (instruction instanceof InvokeDirect) {
- return ((InvokeDirect) instruction).getMethod();
- }
- if (instruction instanceof InvokeDirectRange) {
- return ((InvokeDirectRange) instruction).getMethod();
- }
- if (instruction instanceof InvokeSuper) {
- return ((InvokeSuper) instruction).getMethod();
- }
- if (instruction instanceof InvokeSuperRange) {
- return ((InvokeSuperRange) instruction).getMethod();
- }
- if (instruction instanceof InvokeDirect) {
- return ((InvokeDirect) instruction).getMethod();
- }
- if (instruction instanceof InvokeDirectRange) {
- return ((InvokeDirectRange) instruction).getMethod();
- }
- if (instruction instanceof InvokeStatic) {
- return ((InvokeStatic) instruction).getMethod();
- }
- if (instruction instanceof InvokeStaticRange) {
- return ((InvokeStaticRange) instruction).getMethod();
- }
- assert false;
- return null;
- }
- }
-
- public class FieldAccessInstructionSubject extends InstructionSubject {
-
- FieldAccessInstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
- super(factory, instruction);
- assert isFieldAccess();
- }
-
- public TypeSubject holder() {
- return new TypeSubject(accessedField().getHolder());
- }
-
- public DexField accessedField() {
- if (instruction instanceof Iget) {
- return ((Iget) instruction).getField();
- }
- if (instruction instanceof IgetBoolean) {
- return ((IgetBoolean) instruction).getField();
- }
- if (instruction instanceof IgetByte) {
- return ((IgetByte) instruction).getField();
- }
- if (instruction instanceof IgetShort) {
- return ((IgetShort) instruction).getField();
- }
- if (instruction instanceof IgetChar) {
- return ((IgetChar) instruction).getField();
- }
- if (instruction instanceof IgetWide) {
- return ((IgetWide) instruction).getField();
- }
- if (instruction instanceof IgetObject) {
- return ((IgetObject) instruction).getField();
- }
- if (instruction instanceof Iput) {
- return ((Iput) instruction).getField();
- }
- if (instruction instanceof IputBoolean) {
- return ((IputBoolean) instruction).getField();
- }
- if (instruction instanceof IputByte) {
- return ((IputByte) instruction).getField();
- }
- if (instruction instanceof IputShort) {
- return ((IputShort) instruction).getField();
- }
- if (instruction instanceof IputChar) {
- return ((IputChar) instruction).getField();
- }
- if (instruction instanceof IputWide) {
- return ((IputWide) instruction).getField();
- }
- if (instruction instanceof IputObject) {
- return ((IputObject) instruction).getField();
- }
- if (instruction instanceof Sget) {
- return ((Sget) instruction).getField();
- }
- if (instruction instanceof SgetBoolean) {
- return ((SgetBoolean) instruction).getField();
- }
- if (instruction instanceof SgetByte) {
- return ((SgetByte) instruction).getField();
- }
- if (instruction instanceof SgetShort) {
- return ((SgetShort) instruction).getField();
- }
- if (instruction instanceof SgetChar) {
- return ((SgetChar) instruction).getField();
- }
- if (instruction instanceof SgetWide) {
- return ((SgetWide) instruction).getField();
- }
- if (instruction instanceof SgetObject) {
- return ((SgetObject) instruction).getField();
- }
- if (instruction instanceof Sput) {
- return ((Sput) instruction).getField();
- }
- if (instruction instanceof SputBoolean) {
- return ((SputBoolean) instruction).getField();
- }
- if (instruction instanceof SputByte) {
- return ((SputByte) instruction).getField();
- }
- if (instruction instanceof SputShort) {
- return ((SputShort) instruction).getField();
- }
- if (instruction instanceof SputChar) {
- return ((SputChar) instruction).getField();
- }
- if (instruction instanceof SputWide) {
- return ((SputWide) instruction).getField();
- }
- if (instruction instanceof SputObject) {
- return ((SputObject) instruction).getField();
- }
- assert false;
- return null;
- }
- }
-
- private class InstructionIterator implements Iterator<InstructionSubject> {
-
- private final DexCode code;
- private int index;
-
- InstructionIterator(MethodSubject method) {
- assert method.isPresent();
- this.code = method.getMethod().getCode().asDexCode();
- this.index = 0;
- }
-
- @Override
- public boolean hasNext() {
- return index < code.instructions.length;
- }
-
- @Override
- public InstructionSubject next() {
- if (index == code.instructions.length) {
- throw new NoSuchElementException();
- }
- return factory.create(code.instructions[index++]);
- }
- }
-
- private class FilteredInstructionIterator<T extends InstructionSubject> implements Iterator<T> {
-
- private final InstructionIterator iterator;
- private final Predicate<InstructionSubject> predicate;
- private InstructionSubject pendingNext = null;
-
- FilteredInstructionIterator(MethodSubject method, Predicate<InstructionSubject> predicate) {
- this.iterator = new InstructionIterator(method);
- this.predicate = predicate;
- hasNext();
- }
-
- @Override
- public boolean hasNext() {
- if (pendingNext == null) {
- while (iterator.hasNext()) {
- pendingNext = iterator.next();
- if (predicate.test(pendingNext)) {
- break;
- }
- pendingNext = null;
- }
- }
- return pendingNext != null;
- }
-
- @Override
- public T next() {
- hasNext();
- if (pendingNext == null) {
- throw new NoSuchElementException();
- }
- // We cannot tell if the provided predicate will only match instruction subjects of type T.
- @SuppressWarnings("unchecked")
- T result = (T) pendingNext;
- pendingNext = null;
- return result;
- }
- }
-
- // Build the generic signature using the current mapping if any.
- class GenericSignatureGenerater implements GenericSignatureAction<String> {
-
- private StringBuilder signature;
-
- public String getSignature() {
- return signature.toString();
- }
-
- @Override
- public void parsedSymbol(char symbol) {
- signature.append(symbol);
- }
-
- @Override
- public void parsedIdentifier(String identifier) {
- signature.append(identifier);
- }
-
- @Override
- public String parsedTypeName(String name) {
- String type = name;
- if (originalToObfuscatedMapping != null) {
- String original = originalToObfuscatedMapping.inverse().get(name);
- type = original != null ? original : name;
- }
- signature.append(type);
- return type;
- }
-
- @Override
- public String parsedInnerTypeName(String enclosingType, String name) {
- String type;
- if (originalToObfuscatedMapping != null) {
- // The enclosingType has already been mapped if a mapping is present.
- String minifiedEnclosing = originalToObfuscatedMapping.get(enclosingType);
- type = originalToObfuscatedMapping.inverse().get(minifiedEnclosing + "$" + name);
- if (type != null) {
- assert type.startsWith(enclosingType + "$");
- name = type.substring(enclosingType.length() + 1);
- }
- } else {
- type = enclosingType + "$" + name;
- }
- signature.append(name);
- return type;
- }
-
- @Override
- public void start() {
- signature = new StringBuilder();
- }
-
- @Override
- public void stop() {
- // nothing to do
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentAnnotationSubject.java
new file mode 100644
index 0000000..e6d9306
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentAnnotationSubject.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+
+public class AbsentAnnotationSubject extends AnnotationSubject {
+
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return false;
+ }
+
+ @Override
+ public DexEncodedAnnotation getAnnotation() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentClassSubject.java
new file mode 100644
index 0000000..7213a54
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentClassSubject.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexClass;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class AbsentClassSubject extends ClassSubject {
+
+ private DexInspector dexInspector;
+
+ public AbsentClassSubject(DexInspector dexInspector) {
+ this.dexInspector = dexInspector;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ @Override
+ public void forAllMethods(Consumer<FoundMethodSubject> inspection) {}
+
+ @Override
+ public MethodSubject method(String returnType, String name, List<String> parameters) {
+ return new AbsentMethodSubject();
+ }
+
+ @Override
+ public void forAllFields(Consumer<FoundFieldSubject> inspection) {}
+
+ @Override
+ public FieldSubject field(String type, String name) {
+ return new AbsentFieldSubject();
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return false;
+ }
+
+ @Override
+ public boolean isAnnotation() {
+ return false;
+ }
+
+ @Override
+ public DexClass getDexClass() {
+ return null;
+ }
+
+ @Override
+ public AnnotationSubject annotation(String name) {
+ return new AbsentAnnotationSubject();
+ }
+
+ @Override
+ public String getOriginalName() {
+ return null;
+ }
+
+ @Override
+ public String getOriginalDescriptor() {
+ return null;
+ }
+
+ @Override
+ public String getFinalName() {
+ return null;
+ }
+
+ @Override
+ public String getFinalDescriptor() {
+ return null;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return false;
+ }
+
+ @Override
+ public boolean isMemberClass() {
+ return false;
+ }
+
+ @Override
+ public boolean isLocalClass() {
+ return false;
+ }
+
+ @Override
+ public boolean isAnonymousClass() {
+ return false;
+ }
+
+ @Override
+ public boolean isSynthesizedJavaLambdaClass() {
+ return false;
+ }
+
+ @Override
+ public String getOriginalSignatureAttribute() {
+ return null;
+ }
+
+ @Override
+ public String getFinalSignatureAttribute() {
+ return null;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentFieldSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentFieldSubject.java
new file mode 100644
index 0000000..f79fc00
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentFieldSubject.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+
+public class AbsentFieldSubject extends FieldSubject {
+
+ @Override
+ public boolean isPublic() {
+ return false;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return false;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return false;
+ }
+
+ @Override
+ public Signature getOriginalSignature() {
+ return null;
+ }
+
+ @Override
+ public Signature getFinalSignature() {
+ return null;
+ }
+
+ @Override
+ public boolean hasExplicitStaticValue() {
+ return false;
+ }
+
+ @Override
+ public DexValue getStaticValue() {
+ return null;
+ }
+
+ @Override
+ public DexEncodedField getField() {
+ return null;
+ }
+
+ @Override
+ public String getOriginalSignatureAttribute() {
+ return null;
+ }
+
+ @Override
+ public String getFinalSignatureAttribute() {
+ return null;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentMethodSubject.java
new file mode 100644
index 0000000..5f10dd7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/AbsentMethodSubject.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+
+public class AbsentMethodSubject extends MethodSubject {
+
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return false;
+ }
+
+ @Override
+ public boolean isPublic() {
+ return false;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return false;
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return false;
+ }
+
+ @Override
+ public boolean isBridge() {
+ return false;
+ }
+
+ @Override
+ public boolean isInstanceInitializer() {
+ return false;
+ }
+
+ @Override
+ public boolean isClassInitializer() {
+ return false;
+ }
+
+ @Override
+ public DexEncodedMethod getMethod() {
+ return null;
+ }
+
+ @Override
+ public Signature getOriginalSignature() {
+ return null;
+ }
+
+ @Override
+ public Signature getFinalSignature() {
+ return null;
+ }
+
+ @Override
+ public String getOriginalSignatureAttribute() {
+ return null;
+ }
+
+ @Override
+ public String getFinalSignatureAttribute() {
+ return null;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/AnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/AnnotationSubject.java
new file mode 100644
index 0000000..24682be
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/AnnotationSubject.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+
+public abstract class AnnotationSubject extends Subject {
+
+ public abstract DexEncodedAnnotation getAnnotation();
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/ClassSubject.java
new file mode 100644
index 0000000..afb4928
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/ClassSubject.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public abstract class ClassSubject extends Subject {
+
+ public abstract void forAllMethods(Consumer<FoundMethodSubject> inspection);
+
+ public MethodSubject method(Method method) {
+ List<String> parameters = new ArrayList<>();
+ for (Class<?> parameterType : method.getParameterTypes()) {
+ parameters.add(parameterType.getTypeName());
+ }
+ return method(method.getReturnType().getTypeName(), method.getName(), parameters);
+ }
+
+ public abstract MethodSubject method(String returnType, String name, List<String> parameters);
+
+ public MethodSubject clinit() {
+ return method("void", "<clinit>", ImmutableList.of());
+ }
+
+ public MethodSubject init(List<String> parameters) {
+ return method("void", "<init>", parameters);
+ }
+
+ public MethodSubject method(MethodSignature signature) {
+ return method(signature.type, signature.name, ImmutableList.copyOf(signature.parameters));
+ }
+
+ public MethodSubject method(SmaliBuilder.MethodSignature signature) {
+ return method(
+ signature.returnType, signature.name, ImmutableList.copyOf(signature.parameterTypes));
+ }
+
+ public abstract void forAllFields(Consumer<FoundFieldSubject> inspection);
+
+ public abstract FieldSubject field(String type, String name);
+
+ public abstract boolean isAbstract();
+
+ public abstract boolean isAnnotation();
+
+ public String dumpMethods() {
+ StringBuilder dump = new StringBuilder();
+ forAllMethods(
+ (FoundMethodSubject method) ->
+ dump.append(method.getMethod().toString()).append(method.getMethod().codeToString()));
+ return dump.toString();
+ }
+
+ public abstract DexClass getDexClass();
+
+ public abstract AnnotationSubject annotation(String name);
+
+ public abstract String getOriginalName();
+
+ public abstract String getOriginalDescriptor();
+
+ public abstract String getFinalName();
+
+ public abstract String getFinalDescriptor();
+
+ public abstract boolean isMemberClass();
+
+ public abstract boolean isLocalClass();
+
+ public abstract boolean isAnonymousClass();
+
+ public abstract boolean isSynthesizedJavaLambdaClass();
+
+ public abstract String getOriginalSignatureAttribute();
+
+ public abstract String getFinalSignatureAttribute();
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/DexInspector.java b/src/test/java/com/android/tools/r8/utils/dexinspector/DexInspector.java
new file mode 100644
index 0000000..51e4ce2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/DexInspector.java
@@ -0,0 +1,322 @@
+// Copyright (c) 2016, 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.dexinspector;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.signature.GenericSignatureAction;
+import com.android.tools.r8.naming.signature.GenericSignatureParser;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class DexInspector {
+
+ private final DexApplication application;
+ final DexItemFactory dexItemFactory;
+ private final ClassNameMapper mapping;
+ final BiMap<String, String> originalToObfuscatedMapping;
+
+ public static MethodSignature MAIN =
+ new MethodSignature("main", "void", new String[] {"java.lang.String[]"});
+
+ public DexInspector(Path file, String mappingFile) throws IOException, ExecutionException {
+ this(Collections.singletonList(file), mappingFile);
+ }
+
+ public DexInspector(Path file) throws IOException, ExecutionException {
+ this(Collections.singletonList(file), null);
+ }
+
+ public DexInspector(List<Path> files) throws IOException, ExecutionException {
+ this(files, null);
+ }
+
+ public DexInspector(List<Path> files, String mappingFile) throws IOException, ExecutionException {
+ if (mappingFile != null) {
+ this.mapping = ClassNameMapper.mapperFromFile(Paths.get(mappingFile));
+ originalToObfuscatedMapping = this.mapping.getObfuscatedToOriginalMapping().inverse();
+ } else {
+ this.mapping = null;
+ originalToObfuscatedMapping = null;
+ }
+ Timing timing = new Timing("DexInspector");
+ InternalOptions options = new InternalOptions();
+ dexItemFactory = options.itemFactory;
+ AndroidApp input = AndroidApp.builder().addProgramFiles(files).build();
+ application = new ApplicationReader(input, options, timing).read();
+ }
+
+ public DexInspector(AndroidApp app) throws IOException, ExecutionException {
+ this(
+ new ApplicationReader(app, new InternalOptions(), new Timing("DexInspector"))
+ .read(app.getProguardMapOutputData()));
+ }
+
+ public DexInspector(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
+ throws IOException, ExecutionException {
+ this(
+ new ApplicationReader(app, runOptionsConsumer(optionsConsumer), new Timing("DexInspector"))
+ .read(app.getProguardMapOutputData()));
+ }
+
+ private static InternalOptions runOptionsConsumer(Consumer<InternalOptions> optionsConsumer) {
+ InternalOptions internalOptions = new InternalOptions();
+ optionsConsumer.accept(internalOptions);
+ return internalOptions;
+ }
+
+ public DexInspector(AndroidApp app, Path proguardMap) throws IOException, ExecutionException {
+ this(
+ new ApplicationReader(app, new InternalOptions(), new Timing("DexInspector"))
+ .read(StringResource.fromFile(proguardMap)));
+ }
+
+ public DexInspector(DexApplication application) {
+ dexItemFactory = application.dexItemFactory;
+ this.application = application;
+ this.mapping = application.getProguardMap();
+ originalToObfuscatedMapping =
+ mapping == null ? null : mapping.getObfuscatedToOriginalMapping().inverse();
+ }
+
+ public DexItemFactory getFactory() {
+ return dexItemFactory;
+ }
+
+ DexType toDexType(String string) {
+ return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(string));
+ }
+
+ private DexType toDexTypeIgnorePrimitives(String string) {
+ return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptorIgnorePrimitives(string));
+ }
+
+ static <S, T extends Subject> void forAll(
+ S[] items,
+ BiFunction<S, FoundClassSubject, ? extends T> constructor,
+ FoundClassSubject clazz,
+ Consumer<T> consumer) {
+ for (S item : items) {
+ consumer.accept(constructor.apply(item, clazz));
+ }
+ }
+
+ private static <S, T extends Subject> void forAll(
+ Iterable<S> items, Function<S, T> constructor, Consumer<T> consumer) {
+ for (S item : items) {
+ consumer.accept(constructor.apply(item));
+ }
+ }
+
+ DexAnnotation findAnnotation(String name, DexAnnotationSet annotations) {
+ for (DexAnnotation annotation : annotations.annotations) {
+ DexType type = annotation.annotation.type;
+ String original = mapping == null ? type.toSourceString() : mapping.originalNameOf(type);
+ if (original.equals(name)) {
+ return annotation;
+ }
+ }
+ return null;
+ }
+
+ public String getFinalSignatureAttribute(DexAnnotationSet annotations) {
+ DexAnnotation annotation = findAnnotation("dalvik.annotation.Signature", annotations);
+ if (annotation == null) {
+ return null;
+ }
+ assert annotation.annotation.elements.length == 1;
+ DexAnnotationElement element = annotation.annotation.elements[0];
+ assert element.value instanceof DexValueArray;
+ StringBuilder builder = new StringBuilder();
+ DexValueArray valueArray = (DexValueArray) element.value;
+ for (DexValue value : valueArray.getValues()) {
+ assertTrue(value instanceof DexValueString);
+ DexValueString s = (DexValueString) value;
+ builder.append(s.getValue());
+ }
+ return builder.toString();
+ }
+
+ public String getOriginalSignatureAttribute(
+ DexAnnotationSet annotations, BiConsumer<GenericSignatureParser, String> parse) {
+ String finalSignature = getFinalSignatureAttribute(annotations);
+ if (finalSignature == null || mapping == null) {
+ return finalSignature;
+ }
+
+ GenericSignatureGenerator rewriter = new GenericSignatureGenerator();
+ GenericSignatureParser<String> parser = new GenericSignatureParser<>(rewriter);
+ parse.accept(parser, finalSignature);
+ return rewriter.getSignature();
+ }
+
+ public ClassSubject clazz(Class clazz) {
+ return clazz(clazz.getTypeName());
+ }
+
+ /** Lookup a class by name. This allows both original and obfuscated names. */
+ public ClassSubject clazz(String name) {
+ ClassNamingForNameMapper naming = null;
+ if (mapping != null) {
+ String obfuscated = originalToObfuscatedMapping.get(name);
+ if (obfuscated != null) {
+ naming = mapping.getClassNaming(obfuscated);
+ name = obfuscated;
+ } else {
+ // Figure out if the name is an already obfuscated name.
+ String original = originalToObfuscatedMapping.inverse().get(name);
+ if (original != null) {
+ naming = mapping.getClassNaming(name);
+ }
+ }
+ }
+ DexClass clazz = application.definitionFor(toDexTypeIgnorePrimitives(name));
+ if (clazz == null) {
+ return new AbsentClassSubject(this);
+ }
+ return new FoundClassSubject(this, clazz, naming);
+ }
+
+ public void forAllClasses(Consumer<FoundClassSubject> inspection) {
+ forAll(
+ application.classes(),
+ cls -> {
+ ClassSubject subject = clazz(cls.type.toSourceString());
+ assert subject.isPresent();
+ return (FoundClassSubject) subject;
+ },
+ inspection);
+ }
+
+ public List<FoundClassSubject> allClasses() {
+ ImmutableList.Builder<FoundClassSubject> builder = ImmutableList.builder();
+ forAllClasses(builder::add);
+ return builder.build();
+ }
+
+ public MethodSubject method(Method method) {
+ ClassSubject clazz = clazz(method.getDeclaringClass());
+ if (!clazz.isPresent()) {
+ return new AbsentMethodSubject();
+ }
+ return clazz.method(method);
+ }
+
+ String getObfuscatedTypeName(String originalTypeName) {
+ String obfuscatedType = null;
+ if (mapping != null) {
+ obfuscatedType = originalToObfuscatedMapping.get(originalTypeName);
+ }
+ obfuscatedType = obfuscatedType == null ? originalTypeName : obfuscatedType;
+ return obfuscatedType;
+ }
+
+ InstructionSubject createInstructionSubject(Instruction instruction) {
+ DexInstructionSubject dexInst = new DexInstructionSubject(instruction);
+ if (dexInst.isInvoke()) {
+ return new InvokeDexInstructionSubject(this, instruction);
+ } else if (dexInst.isFieldAccess()) {
+ return new FieldAccessDexInstructionSubject(this, instruction);
+ } else {
+ return dexInst;
+ }
+ }
+
+ InstructionIterator createInstructionIterator(MethodSubject method) {
+ Code code = method.getMethod().getCode();
+ assert code != null && code.isDexCode();
+ return new DexInstructionIterator(this, method);
+ }
+
+ // Build the generic signature using the current mapping if any.
+ class GenericSignatureGenerator implements GenericSignatureAction<String> {
+
+ private StringBuilder signature;
+
+ public String getSignature() {
+ return signature.toString();
+ }
+
+ @Override
+ public void parsedSymbol(char symbol) {
+ signature.append(symbol);
+ }
+
+ @Override
+ public void parsedIdentifier(String identifier) {
+ signature.append(identifier);
+ }
+
+ @Override
+ public String parsedTypeName(String name) {
+ String type = name;
+ if (originalToObfuscatedMapping != null) {
+ String original = originalToObfuscatedMapping.inverse().get(name);
+ type = original != null ? original : name;
+ }
+ signature.append(type);
+ return type;
+ }
+
+ @Override
+ public String parsedInnerTypeName(String enclosingType, String name) {
+ String type;
+ if (originalToObfuscatedMapping != null) {
+ // The enclosingType has already been mapped if a mapping is present.
+ String minifiedEnclosing = originalToObfuscatedMapping.get(enclosingType);
+ type = originalToObfuscatedMapping.inverse().get(minifiedEnclosing + "$" + name);
+ if (type != null) {
+ assert type.startsWith(enclosingType + "$");
+ name = type.substring(enclosingType.length() + 1);
+ }
+ } else {
+ type = enclosingType + "$" + name;
+ }
+ signature.append(name);
+ return type;
+ }
+
+ @Override
+ public void start() {
+ signature = new StringBuilder();
+ }
+
+ @Override
+ public void stop() {
+ // nothing to do
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/DexInstructionIterator.java b/src/test/java/com/android/tools/r8/utils/dexinspector/DexInstructionIterator.java
new file mode 100644
index 0000000..eaff010
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/DexInstructionIterator.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexCode;
+import java.util.NoSuchElementException;
+
+class DexInstructionIterator implements InstructionIterator {
+
+ private DexInspector dexInspector;
+ private final DexCode code;
+ private int index;
+
+ DexInstructionIterator(DexInspector dexInspector, MethodSubject method) {
+ this.dexInspector = dexInspector;
+ assert method.isPresent();
+ Code code = method.getMethod().getCode();
+ assert code != null && code.isDexCode();
+ this.code = code.asDexCode();
+ this.index = 0;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return index < code.instructions.length;
+ }
+
+ @Override
+ public InstructionSubject next() {
+ if (index == code.instructions.length) {
+ throw new NoSuchElementException();
+ }
+ return dexInspector.createInstructionSubject(code.instructions[index++]);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/DexInstructionSubject.java
new file mode 100644
index 0000000..b2320d3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/DexInstructionSubject.java
@@ -0,0 +1,177 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.Goto;
+import com.android.tools.r8.code.IfEqz;
+import com.android.tools.r8.code.IfNez;
+import com.android.tools.r8.code.Iget;
+import com.android.tools.r8.code.IgetBoolean;
+import com.android.tools.r8.code.IgetByte;
+import com.android.tools.r8.code.IgetChar;
+import com.android.tools.r8.code.IgetObject;
+import com.android.tools.r8.code.IgetShort;
+import com.android.tools.r8.code.IgetWide;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.InvokeDirectRange;
+import com.android.tools.r8.code.InvokeInterface;
+import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeStaticRange;
+import com.android.tools.r8.code.InvokeSuper;
+import com.android.tools.r8.code.InvokeSuperRange;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
+import com.android.tools.r8.code.Iput;
+import com.android.tools.r8.code.IputBoolean;
+import com.android.tools.r8.code.IputByte;
+import com.android.tools.r8.code.IputChar;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.IputShort;
+import com.android.tools.r8.code.IputWide;
+import com.android.tools.r8.code.Nop;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.Sget;
+import com.android.tools.r8.code.SgetBoolean;
+import com.android.tools.r8.code.SgetByte;
+import com.android.tools.r8.code.SgetChar;
+import com.android.tools.r8.code.SgetObject;
+import com.android.tools.r8.code.SgetShort;
+import com.android.tools.r8.code.SgetWide;
+import com.android.tools.r8.code.Sput;
+import com.android.tools.r8.code.SputBoolean;
+import com.android.tools.r8.code.SputByte;
+import com.android.tools.r8.code.SputChar;
+import com.android.tools.r8.code.SputObject;
+import com.android.tools.r8.code.SputShort;
+import com.android.tools.r8.code.SputWide;
+import com.android.tools.r8.code.Throw;
+
+public class DexInstructionSubject implements InstructionSubject {
+ protected Instruction instruction;
+
+ public DexInstructionSubject(Instruction instruction) {
+ this.instruction = instruction;
+ }
+
+ @Override
+ public boolean isFieldAccess() {
+ return isInstanceGet() || isInstancePut() || isStaticGet() || isStaticSet();
+ }
+
+ @Override
+ public boolean isInvokeVirtual() {
+ return instruction instanceof InvokeVirtual || instruction instanceof InvokeVirtualRange;
+ }
+
+ @Override
+ public boolean isInvokeInterface() {
+ return instruction instanceof InvokeInterface || instruction instanceof InvokeInterfaceRange;
+ }
+
+ @Override
+ public boolean isInvokeDirect() {
+ return instruction instanceof InvokeDirect || instruction instanceof InvokeDirectRange;
+ }
+
+ @Override
+ public boolean isInvokeSuper() {
+ return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange;
+ }
+
+ @Override
+ public boolean isInvokeStatic() {
+ return instruction instanceof InvokeStatic || instruction instanceof InvokeStaticRange;
+ }
+
+ @Override
+ public boolean isNop() {
+ return instruction instanceof Nop;
+ }
+
+ @Override
+ public boolean isConstString() {
+ return instruction instanceof ConstString;
+ }
+
+ @Override
+ public boolean isConstString(String value) {
+ return instruction instanceof ConstString
+ && ((ConstString) instruction).BBBB.toSourceString().equals(value);
+ }
+
+ @Override
+ public boolean isGoto() {
+
+ return instruction instanceof Goto;
+ }
+
+ @Override
+ public boolean isIfNez() {
+ return instruction instanceof IfNez;
+ }
+
+ @Override
+ public boolean isIfEqz() {
+ return instruction instanceof IfEqz;
+ }
+
+ @Override
+ public boolean isReturnVoid() {
+ return instruction instanceof ReturnVoid;
+ }
+
+ @Override
+ public boolean isThrow() {
+ return instruction instanceof Throw;
+ }
+
+ public boolean isConst4() {
+ return instruction instanceof Const4;
+ }
+
+ public boolean isInstanceGet() {
+ return instruction instanceof Iget
+ || instruction instanceof IgetBoolean
+ || instruction instanceof IgetByte
+ || instruction instanceof IgetShort
+ || instruction instanceof IgetChar
+ || instruction instanceof IgetWide
+ || instruction instanceof IgetObject;
+ }
+
+ public boolean isInstancePut() {
+ return instruction instanceof Iput
+ || instruction instanceof IputBoolean
+ || instruction instanceof IputByte
+ || instruction instanceof IputShort
+ || instruction instanceof IputChar
+ || instruction instanceof IputWide
+ || instruction instanceof IputObject;
+ }
+
+ public boolean isStaticGet() {
+ return instruction instanceof Sget
+ || instruction instanceof SgetBoolean
+ || instruction instanceof SgetByte
+ || instruction instanceof SgetShort
+ || instruction instanceof SgetChar
+ || instruction instanceof SgetWide
+ || instruction instanceof SgetObject;
+ }
+
+ public boolean isStaticSet() {
+ return instruction instanceof Sput
+ || instruction instanceof SputBoolean
+ || instruction instanceof SputByte
+ || instruction instanceof SputShort
+ || instruction instanceof SputChar
+ || instruction instanceof SputWide
+ || instruction instanceof SputObject;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/FieldAccessDexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/FieldAccessDexInstructionSubject.java
new file mode 100644
index 0000000..c3ea3fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/FieldAccessDexInstructionSubject.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.code.Instruction;
+
+public class FieldAccessDexInstructionSubject extends DexInstructionSubject
+ implements FieldAccessInstructionSubject {
+
+ private DexInspector dexInspector;
+
+ public FieldAccessDexInstructionSubject(DexInspector dexInspector, Instruction instruction) {
+ super(instruction);
+ this.dexInspector = dexInspector;
+ assert isFieldAccess();
+ }
+
+ public TypeSubject holder() {
+ return new TypeSubject(dexInspector, instruction.getField().getHolder());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/FieldAccessInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/FieldAccessInstructionSubject.java
new file mode 100644
index 0000000..54b27c7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/FieldAccessInstructionSubject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+public interface FieldAccessInstructionSubject extends InstructionSubject {
+ TypeSubject holder();
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/FieldSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/FieldSubject.java
new file mode 100644
index 0000000..1e9471b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/FieldSubject.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexValue;
+
+public abstract class FieldSubject extends MemberSubject {
+ public abstract boolean hasExplicitStaticValue();
+
+ public abstract DexEncodedField getField();
+
+ public abstract DexValue getStaticValue();
+
+ public abstract boolean isRenamed();
+
+ public abstract String getOriginalSignatureAttribute();
+
+ public abstract String getFinalSignatureAttribute();
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/FilteredInstructionIterator.java b/src/test/java/com/android/tools/r8/utils/dexinspector/FilteredInstructionIterator.java
new file mode 100644
index 0000000..33ac77f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/FilteredInstructionIterator.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.function.Predicate;
+
+class FilteredInstructionIterator<T extends InstructionSubject> implements Iterator<T> {
+
+ private DexInspector dexInspector;
+ private final InstructionIterator iterator;
+ private final Predicate<InstructionSubject> predicate;
+ private InstructionSubject pendingNext = null;
+
+ FilteredInstructionIterator(
+ DexInspector dexInspector, MethodSubject method, Predicate<InstructionSubject> predicate) {
+ this.dexInspector = dexInspector;
+ this.iterator = dexInspector.createInstructionIterator(method);
+ this.predicate = predicate;
+ hasNext();
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (pendingNext == null) {
+ while (iterator.hasNext()) {
+ pendingNext = iterator.next();
+ if (predicate.test(pendingNext)) {
+ break;
+ }
+ pendingNext = null;
+ }
+ }
+ return pendingNext != null;
+ }
+
+ @Override
+ public T next() {
+ hasNext();
+ if (pendingNext == null) {
+ throw new NoSuchElementException();
+ }
+ // We cannot tell if the provided predicate will only match instruction subjects of type T.
+ @SuppressWarnings("unchecked")
+ T result = (T) pendingNext;
+ pendingNext = null;
+ return result;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/FoundAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/FoundAnnotationSubject.java
new file mode 100644
index 0000000..ed85b24
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/FoundAnnotationSubject.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+
+public class FoundAnnotationSubject extends AnnotationSubject {
+
+ private final DexAnnotation annotation;
+
+ public FoundAnnotationSubject(DexAnnotation annotation) {
+ this.annotation = annotation;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return false;
+ }
+
+ @Override
+ public DexEncodedAnnotation getAnnotation() {
+ return annotation.annotation;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/FoundClassSubject.java
new file mode 100644
index 0000000..12dbaeb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/FoundClassSubject.java
@@ -0,0 +1,253 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.signature.GenericSignatureParser;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class FoundClassSubject extends ClassSubject {
+
+ private DexInspector dexInspector;
+ private final DexClass dexClass;
+ final ClassNamingForNameMapper naming;
+
+ FoundClassSubject(DexInspector dexInspector, DexClass dexClass, ClassNamingForNameMapper naming) {
+ this.dexInspector = dexInspector;
+ this.dexClass = dexClass;
+ this.naming = naming;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
+ DexInspector.forAll(
+ dexClass.directMethods(),
+ (encoded, clazz) -> new FoundMethodSubject(dexInspector, encoded, clazz),
+ this,
+ inspection);
+ DexInspector.forAll(
+ dexClass.virtualMethods(),
+ (encoded, clazz) -> new FoundMethodSubject(dexInspector, encoded, clazz),
+ this,
+ inspection);
+ }
+
+ @Override
+ public MethodSubject method(String returnType, String name, List<String> parameters) {
+ DexType[] parameterTypes = new DexType[parameters.size()];
+ for (int i = 0; i < parameters.size(); i++) {
+ parameterTypes[i] =
+ dexInspector.toDexType(dexInspector.getObfuscatedTypeName(parameters.get(i)));
+ }
+ DexProto proto =
+ dexInspector.dexItemFactory.createProto(
+ dexInspector.toDexType(dexInspector.getObfuscatedTypeName(returnType)), parameterTypes);
+ if (naming != null) {
+ String[] parameterStrings = new String[parameterTypes.length];
+ Signature signature =
+ new MethodSignature(name, returnType, parameters.toArray(parameterStrings));
+ MemberNaming methodNaming = naming.lookupByOriginalSignature(signature);
+ if (methodNaming != null) {
+ name = methodNaming.getRenamedName();
+ }
+ }
+ DexMethod dexMethod =
+ dexInspector.dexItemFactory.createMethod(
+ dexClass.type, proto, dexInspector.dexItemFactory.createString(name));
+ DexEncodedMethod encoded = findMethod(dexClass.directMethods(), dexMethod);
+ if (encoded == null) {
+ encoded = findMethod(dexClass.virtualMethods(), dexMethod);
+ }
+ return encoded == null
+ ? new AbsentMethodSubject()
+ : new FoundMethodSubject(dexInspector, encoded, this);
+ }
+
+ private DexEncodedMethod findMethod(DexEncodedMethod[] methods, DexMethod dexMethod) {
+ for (DexEncodedMethod method : methods) {
+ if (method.method.equals(dexMethod)) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void forAllFields(Consumer<FoundFieldSubject> inspection) {
+ DexInspector.forAll(
+ dexClass.staticFields(),
+ (dexField, clazz) -> new FoundFieldSubject(dexInspector, dexField, clazz),
+ this,
+ inspection);
+ DexInspector.forAll(
+ dexClass.instanceFields(),
+ (dexField, clazz) -> new FoundFieldSubject(dexInspector, dexField, clazz),
+ this,
+ inspection);
+ }
+
+ @Override
+ public FieldSubject field(String type, String name) {
+ String obfuscatedType = dexInspector.getObfuscatedTypeName(type);
+ MemberNaming fieldNaming = null;
+ if (naming != null) {
+ fieldNaming = naming.lookupByOriginalSignature(new FieldSignature(name, type));
+ }
+ String obfuscatedName = fieldNaming == null ? name : fieldNaming.getRenamedName();
+
+ DexField field =
+ dexInspector.dexItemFactory.createField(
+ dexClass.type,
+ dexInspector.toDexType(obfuscatedType),
+ dexInspector.dexItemFactory.createString(obfuscatedName));
+ DexEncodedField encoded = findField(dexClass.staticFields(), field);
+ if (encoded == null) {
+ encoded = findField(dexClass.instanceFields(), field);
+ }
+ return encoded == null
+ ? new AbsentFieldSubject()
+ : new FoundFieldSubject(dexInspector, encoded, this);
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return dexClass.accessFlags.isAbstract();
+ }
+
+ @Override
+ public boolean isAnnotation() {
+ return dexClass.accessFlags.isAnnotation();
+ }
+
+ private DexEncodedField findField(DexEncodedField[] fields, DexField dexField) {
+ for (DexEncodedField field : fields) {
+ if (field.field.equals(dexField)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public DexClass getDexClass() {
+ return dexClass;
+ }
+
+ @Override
+ public AnnotationSubject annotation(String name) {
+ // Ensure we don't check for annotations represented as attributes.
+ assert !name.endsWith("EnclosingClass")
+ && !name.endsWith("EnclosingMethod")
+ && !name.endsWith("InnerClass");
+ DexAnnotation annotation = dexInspector.findAnnotation(name, dexClass.annotations);
+ return annotation == null
+ ? new AbsentAnnotationSubject()
+ : new FoundAnnotationSubject(annotation);
+ }
+
+ @Override
+ public String getOriginalName() {
+ if (naming != null) {
+ return naming.originalName;
+ } else {
+ return getFinalName();
+ }
+ }
+
+ @Override
+ public String getOriginalDescriptor() {
+ if (naming != null) {
+ return DescriptorUtils.javaTypeToDescriptor(naming.originalName);
+ } else {
+ return getFinalDescriptor();
+ }
+ }
+
+ @Override
+ public String getFinalName() {
+ return DescriptorUtils.descriptorToJavaType(getFinalDescriptor());
+ }
+
+ @Override
+ public String getFinalDescriptor() {
+ return dexClass.type.descriptor.toString();
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return naming != null && !getFinalDescriptor().equals(getOriginalDescriptor());
+ }
+
+ private InnerClassAttribute getInnerClassAttribute() {
+ for (InnerClassAttribute innerClassAttribute : dexClass.getInnerClasses()) {
+ if (dexClass.type == innerClassAttribute.getInner()) {
+ return innerClassAttribute;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isLocalClass() {
+ InnerClassAttribute innerClass = getInnerClassAttribute();
+ return innerClass != null && innerClass.isNamed() && dexClass.getEnclosingMethod() != null;
+ }
+
+ @Override
+ public boolean isMemberClass() {
+ InnerClassAttribute innerClass = getInnerClassAttribute();
+ return innerClass != null
+ && innerClass.getOuter() != null
+ && innerClass.isNamed()
+ && dexClass.getEnclosingMethod() == null;
+ }
+
+ @Override
+ public boolean isAnonymousClass() {
+ InnerClassAttribute innerClass = getInnerClassAttribute();
+ return innerClass != null && innerClass.isAnonymous() && dexClass.getEnclosingMethod() != null;
+ }
+
+ @Override
+ public boolean isSynthesizedJavaLambdaClass() {
+ return dexClass.type.getName().contains("$Lambda$");
+ }
+
+ @Override
+ public String getOriginalSignatureAttribute() {
+ return dexInspector.getOriginalSignatureAttribute(
+ dexClass.annotations, GenericSignatureParser::parseClassSignature);
+ }
+
+ @Override
+ public String getFinalSignatureAttribute() {
+ return dexInspector.getFinalSignatureAttribute(dexClass.annotations);
+ }
+
+ @Override
+ public String toString() {
+ return dexClass.toSourceString();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/FoundFieldSubject.java
new file mode 100644
index 0000000..52aa0ce
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/FoundFieldSubject.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.signature.GenericSignatureParser;
+
+public class FoundFieldSubject extends FieldSubject {
+
+ private DexInspector dexInspector;
+ private final FoundClassSubject clazz;
+ private final DexEncodedField dexField;
+
+ public FoundFieldSubject(
+ DexInspector dexInspector, DexEncodedField dexField, FoundClassSubject clazz) {
+ this.dexInspector = dexInspector;
+ this.clazz = clazz;
+ this.dexField = dexField;
+ }
+
+ @Override
+ public boolean isPublic() {
+ return dexField.accessFlags.isPublic();
+ }
+
+ @Override
+ public boolean isStatic() {
+ return dexField.accessFlags.isStatic();
+ }
+
+ @Override
+ public boolean isFinal() {
+ return dexField.accessFlags.isFinal();
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return clazz.naming != null && !getFinalSignature().name.equals(getOriginalSignature().name);
+ }
+
+ public TypeSubject type() {
+ return new TypeSubject(dexInspector, dexField.field.type);
+ }
+
+ @Override
+ public FieldSignature getOriginalSignature() {
+ FieldSignature signature = getFinalSignature();
+ if (clazz.naming == null) {
+ return signature;
+ }
+
+ // Map the type to the original name. This is needed as the in the Proguard map the
+ // names on the left side are the original names. E.g.
+ //
+ // X -> a
+ // X field -> a
+ //
+ // whereas the final signature is for X.a is "a a"
+ String obfuscatedType = signature.type;
+ String originalType = dexInspector.originalToObfuscatedMapping.inverse().get(obfuscatedType);
+ String fieldType = originalType != null ? originalType : obfuscatedType;
+
+ FieldSignature lookupSignature = new FieldSignature(signature.name, fieldType);
+
+ MemberNaming memberNaming = clazz.naming.lookup(lookupSignature);
+ return memberNaming != null ? (FieldSignature) memberNaming.getOriginalSignature() : signature;
+ }
+
+ @Override
+ public FieldSignature getFinalSignature() {
+ return FieldSignature.fromDexField(dexField.field);
+ }
+
+ @Override
+ public boolean hasExplicitStaticValue() {
+ return isStatic() && dexField.hasExplicitStaticValue();
+ }
+
+ @Override
+ public DexValue getStaticValue() {
+ return dexField.getStaticValue();
+ }
+
+ @Override
+ public DexEncodedField getField() {
+ return dexField;
+ }
+
+ @Override
+ public String getOriginalSignatureAttribute() {
+ return dexInspector.getOriginalSignatureAttribute(
+ dexField.annotations, GenericSignatureParser::parseFieldSignature);
+ }
+
+ @Override
+ public String getFinalSignatureAttribute() {
+ return dexInspector.getFinalSignatureAttribute(dexField.annotations);
+ }
+
+ @Override
+ public String toString() {
+ return dexField.toSourceString();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/FoundMethodSubject.java
new file mode 100644
index 0000000..e245258
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/FoundMethodSubject.java
@@ -0,0 +1,140 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.signature.GenericSignatureParser;
+import java.util.Iterator;
+import java.util.function.Predicate;
+
+public class FoundMethodSubject extends MethodSubject {
+
+ private DexInspector dexInspector;
+ private final FoundClassSubject clazz;
+ private final DexEncodedMethod dexMethod;
+
+ public FoundMethodSubject(
+ DexInspector dexInspector, DexEncodedMethod encoded, FoundClassSubject clazz) {
+ this.dexInspector = dexInspector;
+ this.clazz = clazz;
+ this.dexMethod = encoded;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return clazz.naming != null && !getFinalSignature().name.equals(getOriginalSignature().name);
+ }
+
+ @Override
+ public boolean isPublic() {
+ return dexMethod.accessFlags.isPublic();
+ }
+
+ @Override
+ public boolean isStatic() {
+ return dexMethod.accessFlags.isStatic();
+ }
+
+ @Override
+ public boolean isFinal() {
+ return dexMethod.accessFlags.isFinal();
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return dexMethod.accessFlags.isAbstract();
+ }
+
+ @Override
+ public boolean isBridge() {
+ return dexMethod.accessFlags.isBridge();
+ }
+
+ @Override
+ public boolean isInstanceInitializer() {
+ return dexMethod.isInstanceInitializer();
+ }
+
+ @Override
+ public boolean isClassInitializer() {
+ return dexMethod.isClassInitializer();
+ }
+
+ @Override
+ public DexEncodedMethod getMethod() {
+ return dexMethod;
+ }
+
+ @Override
+ public MethodSignature getOriginalSignature() {
+ MethodSignature signature = getFinalSignature();
+ if (clazz.naming == null) {
+ return signature;
+ }
+
+ // Map the parameters and return type to original names. This is needed as the in the
+ // Proguard map the names on the left side are the original names. E.g.
+ //
+ // X -> a
+ // X method(X) -> a
+ //
+ // whereas the final signature is for X.a is "a (a)"
+ String[] OriginalParameters = new String[signature.parameters.length];
+ for (int i = 0; i < OriginalParameters.length; i++) {
+ String obfuscated = signature.parameters[i];
+ String original = dexInspector.originalToObfuscatedMapping.inverse().get(obfuscated);
+ OriginalParameters[i] = original != null ? original : obfuscated;
+ }
+ String obfuscatedReturnType = signature.type;
+ String originalReturnType =
+ dexInspector.originalToObfuscatedMapping.inverse().get(obfuscatedReturnType);
+ String returnType = originalReturnType != null ? originalReturnType : obfuscatedReturnType;
+
+ MethodSignature lookupSignature =
+ new MethodSignature(signature.name, returnType, OriginalParameters);
+
+ MemberNaming memberNaming = clazz.naming.lookup(lookupSignature);
+ return memberNaming != null ? (MethodSignature) memberNaming.getOriginalSignature() : signature;
+ }
+
+ @Override
+ public MethodSignature getFinalSignature() {
+ return MethodSignature.fromDexMethod(dexMethod.method);
+ }
+
+ @Override
+ public String getOriginalSignatureAttribute() {
+ return dexInspector.getOriginalSignatureAttribute(
+ dexMethod.annotations, GenericSignatureParser::parseMethodSignature);
+ }
+
+ @Override
+ public String getFinalSignatureAttribute() {
+ return dexInspector.getFinalSignatureAttribute(dexMethod.annotations);
+ }
+
+ @Override
+ public Iterator<InstructionSubject> iterateInstructions() {
+ return dexInspector.createInstructionIterator(this);
+ }
+
+ @Override
+ public <T extends InstructionSubject> Iterator<T> iterateInstructions(
+ Predicate<InstructionSubject> filter) {
+ return new FilteredInstructionIterator<>(dexInspector, this, filter);
+ }
+
+ @Override
+ public String toString() {
+ return dexMethod.toSourceString();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/InstructionIterator.java b/src/test/java/com/android/tools/r8/utils/dexinspector/InstructionIterator.java
new file mode 100644
index 0000000..a777319
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/InstructionIterator.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import java.util.Iterator;
+
+interface InstructionIterator extends Iterator<InstructionSubject> {}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/InstructionSubject.java
new file mode 100644
index 0000000..00102e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/InstructionSubject.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+public interface InstructionSubject {
+ boolean isFieldAccess();
+
+ boolean isInvokeVirtual();
+
+ boolean isInvokeInterface();
+
+ boolean isInvokeDirect();
+
+ boolean isInvokeSuper();
+
+ boolean isInvokeStatic();
+
+ boolean isNop();
+
+ boolean isConstString();
+
+ boolean isConstString(String value);
+
+ boolean isGoto();
+
+ boolean isIfNez();
+
+ boolean isIfEqz();
+
+ boolean isReturnVoid();
+
+ boolean isThrow();
+
+ default boolean isInvoke() {
+ return isInvokeVirtual()
+ || isInvokeInterface()
+ || isInvokeDirect()
+ || isInvokeSuper()
+ || isInvokeStatic();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/InvokeDexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/InvokeDexInstructionSubject.java
new file mode 100644
index 0000000..a4f5636
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/InvokeDexInstructionSubject.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.DexMethod;
+
+public class InvokeDexInstructionSubject extends DexInstructionSubject
+ implements InvokeInstructionSubject {
+
+ private DexInspector dexInspector;
+
+ public InvokeDexInstructionSubject(DexInspector dexInspector, Instruction instruction) {
+ super(instruction);
+ this.dexInspector = dexInspector;
+ assert isInvoke();
+ }
+
+ public TypeSubject holder() {
+ return new TypeSubject(dexInspector, invokedMethod().getHolder());
+ }
+
+ public DexMethod invokedMethod() {
+ return instruction.getMethod();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/InvokeInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/InvokeInstructionSubject.java
new file mode 100644
index 0000000..9b59340
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/InvokeInstructionSubject.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexMethod;
+
+public interface InvokeInstructionSubject extends InstructionSubject {
+ TypeSubject holder();
+
+ DexMethod invokedMethod();
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspectorMatchers.java b/src/test/java/com/android/tools/r8/utils/dexinspector/Matchers.java
similarity index 93%
rename from src/test/java/com/android/tools/r8/utils/DexInspectorMatchers.java
rename to src/test/java/com/android/tools/r8/utils/dexinspector/Matchers.java
index 1986bbc..ab3ef13 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspectorMatchers.java
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/Matchers.java
@@ -2,18 +2,14 @@
// 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;
+package com.android.tools.r8.utils.dexinspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.FieldSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
-import com.android.tools.r8.utils.DexInspector.Subject;
import com.google.common.collect.ImmutableList;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
-public class DexInspectorMatchers {
+public class Matchers {
private static String type(Subject subject) {
String type = "<unknown subject type>";
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/MemberSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/MemberSubject.java
new file mode 100644
index 0000000..1852659
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/MemberSubject.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.naming.MemberNaming.Signature;
+
+public abstract class MemberSubject extends Subject {
+
+ public abstract boolean isPublic();
+
+ public abstract boolean isStatic();
+
+ public abstract boolean isFinal();
+
+ public abstract Signature getOriginalSignature();
+
+ public abstract Signature getFinalSignature();
+
+ public String getOriginalName() {
+ Signature originalSignature = getOriginalSignature();
+ return originalSignature == null ? null : originalSignature.name;
+ }
+
+ public String getFinalName() {
+ Signature finalSignature = getFinalSignature();
+ return finalSignature == null ? null : finalSignature.name;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/MethodSubject.java
new file mode 100644
index 0000000..3427924
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/MethodSubject.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import java.util.Iterator;
+import java.util.function.Predicate;
+
+public abstract class MethodSubject extends MemberSubject {
+
+ public abstract boolean isAbstract();
+
+ public abstract boolean isBridge();
+
+ public abstract boolean isInstanceInitializer();
+
+ public abstract boolean isClassInitializer();
+
+ public abstract String getOriginalSignatureAttribute();
+
+ public abstract String getFinalSignatureAttribute();
+
+ public abstract DexEncodedMethod getMethod();
+
+ public Iterator<InstructionSubject> iterateInstructions() {
+ return null;
+ }
+
+ public <T extends InstructionSubject> Iterator<T> iterateInstructions(
+ Predicate<InstructionSubject> filter) {
+ return null;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/Subject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/Subject.java
new file mode 100644
index 0000000..f83eb14
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/Subject.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+public abstract class Subject {
+
+ public abstract boolean isPresent();
+
+ public abstract boolean isRenamed();
+}
diff --git a/src/test/java/com/android/tools/r8/utils/dexinspector/TypeSubject.java b/src/test/java/com/android/tools/r8/utils/dexinspector/TypeSubject.java
new file mode 100644
index 0000000..6a911eb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/dexinspector/TypeSubject.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.dexinspector;
+
+import com.android.tools.r8.graph.DexType;
+
+public class TypeSubject extends Subject {
+
+ private DexInspector dexInspector;
+ private final DexType dexType;
+
+ TypeSubject(DexInspector dexInspector, DexType dexType) {
+ this.dexInspector = dexInspector;
+ this.dexType = dexType;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return false;
+ }
+
+ public boolean is(String type) {
+ return dexType.equals(dexInspector.toDexType(type));
+ }
+
+ public String toString() {
+ return dexType.toSourceString();
+ }
+}