Version 1.4.27
Cherry pick: Disallow changing prototype of methods that override interface method
CL: https://r8-review.googlesource.com/c/r8/+/33226
Cherry pick: Explicitly lookup synthesized class in GraphLense.mapDexEncodedMethod()
CL: https://r8-review.googlesource.com/c/r8/+/33221
Cherry pick: Avoid merging classes if target has static initialization
CL: https://r8-review.googlesource.com/c/r8/+/33141
Cherry pick: Negate broken assertion
CL: https://r8-review.googlesource.com/c/r8/+/33142
Cherry pick: Map context during graph lense lookup
CL: https://r8-review.googlesource.com/c/r8/+/33112
Bug: 123012641, 122275808, 123158783
Change-Id: I2f6e632f189e5dd7a8fc1f43804a272c7ee41c54
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 128245f..30db140 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.4.26";
+ public static final String LABEL = "1.4.27";
private Version() {
}
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 c76865e..d9940f3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -448,6 +448,13 @@
}
public boolean classInitializationMayHaveSideEffects(AppInfo appInfo) {
+ return classInitializationMayHaveSideEffects(appInfo, Predicates.alwaysFalse());
+ }
+
+ public boolean classInitializationMayHaveSideEffects(AppInfo appInfo, Predicate<DexType> ignore) {
+ if (ignore.test(type)) {
+ return false;
+ }
if (hasNonTrivialClassInitializer()) {
return true;
}
@@ -455,11 +462,11 @@
return true;
}
for (DexType iface : interfaces.values) {
- if (iface.classInitializationMayHaveSideEffects(appInfo)) {
+ if (iface.classInitializationMayHaveSideEffects(appInfo, ignore)) {
return true;
}
}
- if (superType != null && superType.classInitializationMayHaveSideEffects(appInfo)) {
+ if (superType != null && superType.classInitializationMayHaveSideEffects(appInfo, ignore)) {
return true;
}
return false;
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 1d7555d..d5e2fed 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -17,6 +17,7 @@
import java.util.TreeSet;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Predicate;
public class DexType extends DexReference implements PresortedComparable<DexType> {
@@ -113,9 +114,9 @@
return implementedInterfaces(appInfo).contains(appInfo.dexItemFactory.serializableType);
}
- public boolean classInitializationMayHaveSideEffects(AppInfo appInfo) {
+ public boolean classInitializationMayHaveSideEffects(AppInfo appInfo, Predicate<DexType> ignore) {
DexClass clazz = appInfo.definitionFor(this);
- return clazz == null || clazz.classInitializationMayHaveSideEffects(appInfo);
+ return clazz == null || clazz.classInitializationMayHaveSideEffects(appInfo, ignore);
}
public boolean isUnknown() {
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 012c4a4..09094aa 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -73,22 +73,24 @@
}
public DirectMappedDexApplication rewrittenWithLense(GraphLense graphLense) {
- assert mappingIsValid(graphLense, programClasses.getAllTypes());
- assert mappingIsValid(graphLense, libraryClasses.keySet());
// As a side effect, this will rebuild the program classes and library classes maps.
- return this.builder().build().asDirect();
+ DirectMappedDexApplication rewrittenApplication = this.builder().build().asDirect();
+ assert rewrittenApplication.mappingIsValid(graphLense, programClasses.getAllTypes());
+ assert rewrittenApplication.mappingIsValid(graphLense, libraryClasses.keySet());
+ return rewrittenApplication;
}
private boolean mappingIsValid(GraphLense graphLense, Iterable<DexType> types) {
- // The lense might either map to a different type that is already present in the application
+ // The lens might either map to a different type that is already present in the application
// (e.g. relinking a type) or it might encode a type that was renamed, in which case the
// original type will point to a definition that was renamed.
for (DexType type : types) {
DexType renamed = graphLense.lookupType(type);
if (renamed != type) {
- if (definitionFor(type).type != renamed && definitionFor(renamed) == null) {
- return false;
+ if (definitionFor(type) == null && definitionFor(renamed) != null) {
+ continue;
}
+ assert definitionFor(type).type == renamed || definitionFor(renamed) != null;
}
}
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 9a373fa..c6a527f 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -356,6 +356,8 @@
return new Builder();
}
+ public abstract DexType getOriginalType(DexType type);
+
public abstract DexField getOriginalFieldSignature(DexField field);
public abstract DexMethod getOriginalMethodSignature(DexMethod method);
@@ -365,18 +367,27 @@
public abstract DexMethod getRenamedMethodSignature(DexMethod originalMethod);
public DexEncodedMethod mapDexEncodedMethod(
- AppInfo appInfo, DexEncodedMethod originalEncodedMethod) {
+ DexEncodedMethod originalEncodedMethod,
+ AppInfo appInfo,
+ Map<DexType, DexProgramClass> synthesizedClasses) {
DexMethod newMethod = getRenamedMethodSignature(originalEncodedMethod.method);
- if (newMethod != originalEncodedMethod.method) {
- // We can't directly use AppInfo#definitionFor(DexMethod) since definitions may not be
- // updated either yet.
- DexClass newHolder = appInfo.definitionFor(newMethod.holder);
- assert newHolder != null;
- DexEncodedMethod newEncodedMethod = newHolder.lookupMethod(newMethod);
- assert newEncodedMethod != null;
- return newEncodedMethod;
+ // Note that:
+ // * Even if `newMethod` is the same as `originalEncodedMethod.method`, we still need to look it
+ // up, since `originalEncodedMethod` may be obsolete.
+ // * We can't directly use AppInfo#definitionFor(DexMethod) since definitions may not be
+ // updated either yet.
+ DexClass newHolder = appInfo.definitionFor(newMethod.holder);
+
+ // TODO(b/120130831): Need to ensure that all synthesized classes are part of the application.
+ if (newHolder == null) {
+ newHolder = synthesizedClasses.get(newMethod.holder);
}
- return originalEncodedMethod;
+
+ assert newHolder != null;
+
+ DexEncodedMethod newEncodedMethod = newHolder.lookupMethod(newMethod);
+ assert newEncodedMethod != null;
+ return newEncodedMethod;
}
public abstract DexType lookupType(DexType type);
@@ -388,7 +399,7 @@
}
public abstract GraphLenseLookupResult lookupMethod(
- DexMethod method, DexEncodedMethod context, Type type);
+ DexMethod method, DexMethod context, Type type);
public abstract RewrittenPrototypeDescription lookupPrototypeChanges(DexMethod method);
@@ -547,6 +558,11 @@
}
@Override
+ public DexType getOriginalType(DexType type) {
+ return type;
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
return field;
}
@@ -572,8 +588,7 @@
}
@Override
- public GraphLenseLookupResult lookupMethod(
- DexMethod method, DexEncodedMethod context, Type type) {
+ public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
return new GraphLenseLookupResult(method, type);
}
@@ -594,14 +609,14 @@
}
/**
- * GraphLense implementation with a parent lense using a simple mapping for type, method and
- * field mapping.
+ * GraphLense implementation with a parent lense using a simple mapping for type, method and field
+ * mapping.
*
- * Subclasses can override the lookup methods.
+ * <p>Subclasses can override the lookup methods.
*
- * For method mapping where invocation type can change just override
- * {@link #mapInvocationType(DexMethod, DexMethod, DexEncodedMethod, Type)} if
- * the default name mapping applies, and only invocation type might need to change.
+ * <p>For method mapping where invocation type can change just override {@link
+ * #mapInvocationType(DexMethod, DexMethod, DexMethod, Type)} if the default name mapping applies,
+ * and only invocation type might need to change.
*/
public static class NestedGraphLense extends GraphLense {
@@ -636,6 +651,11 @@
}
@Override
+ public DexType getOriginalType(DexType type) {
+ return previousLense.getOriginalType(type);
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
DexField originalField =
originalFieldSignatures != null
@@ -693,9 +713,12 @@
}
@Override
- public GraphLenseLookupResult lookupMethod(
- DexMethod method, DexEncodedMethod context, Type type) {
- GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
+ public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+ DexMethod previousContext =
+ originalMethodSignatures != null
+ ? originalMethodSignatures.getOrDefault(context, context)
+ : context;
+ GraphLenseLookupResult previous = previousLense.lookupMethod(method, previousContext, type);
DexMethod newMethod = methodMap.get(previous.getMethod());
if (newMethod == null) {
return previous;
@@ -703,7 +726,7 @@
// TODO(sgjesse): Should we always do interface to virtual mapping? Is it a performance win
// that only subclasses which are known to need it actually do it?
return new GraphLenseLookupResult(
- newMethod, mapInvocationType(newMethod, method, context, previous.getType()));
+ newMethod, mapInvocationType(newMethod, method, previous.getType()));
}
@Override
@@ -714,22 +737,20 @@
/**
* Default invocation type mapping.
*
- * This is an identity mapping. If a subclass need invocation type mapping either override
- * this method or {@link #lookupMethod(DexMethod, DexEncodedMethod, Type)}
+ * <p>This is an identity mapping. If a subclass need invocation type mapping either override
+ * this method or {@link #lookupMethod(DexMethod, DexMethod, Type)}
*/
- protected Type mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
+ protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
return type;
}
/**
* Standard mapping between interface and virtual invoke type.
*
- * Handle methods moved from interface to class or class to interface.
+ * <p>Handle methods moved from interface to class or class to interface.
*/
- final protected Type mapVirtualInterfaceInvocationTypes(
- AppInfo appInfo, DexMethod newMethod, DexMethod originalMethod,
- DexEncodedMethod context, Type type) {
+ protected final Type mapVirtualInterfaceInvocationTypes(
+ AppInfo appInfo, DexMethod newMethod, DexMethod originalMethod, Type type) {
if (type == Type.VIRTUAL || type == Type.INTERFACE) {
// Get the invoke type of the actual definition.
DexClass newTargetClass = appInfo.definitionFor(newMethod.holder);
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 d9040c5..c12ea40 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
@@ -532,7 +532,7 @@
private void processInvoke(Type type, DexMethod method) {
DexEncodedMethod source = caller.method;
- GraphLenseLookupResult result = graphLense.lookupMethod(method, source, type);
+ GraphLenseLookupResult result = graphLense.lookupMethod(method, source.method, type);
method = result.getMethod();
type = result.getType();
DexEncodedMethod definition = appInfo.lookup(type, method, source.method.holder);
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 4ecda95..9cb4d46 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
@@ -89,6 +89,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -359,9 +360,18 @@
InterfaceMethodRewriter.Flavor includeAllResources,
ExecutorService executorService)
throws ExecutionException {
+ desugarInterfaceMethods(builder, includeAllResources, executorService, null);
+ }
+
+ private void desugarInterfaceMethods(
+ Builder<?> builder,
+ InterfaceMethodRewriter.Flavor includeAllResources,
+ ExecutorService executorService,
+ Map<DexType, DexProgramClass> synthesizedClasses)
+ throws ExecutionException {
if (interfaceMethodRewriter != null) {
interfaceMethodRewriter.desugarInterfaceMethods(
- builder, includeAllResources, executorService);
+ builder, includeAllResources, executorService, synthesizedClasses);
}
}
@@ -583,18 +593,21 @@
synthesizeLambdaClasses(builder, executorService);
printPhase("Interface method desugaring");
- desugarInterfaceMethods(builder, IncludeAllResources, executorService);
+ Map<DexType, DexProgramClass> synthesizedClasses = new IdentityHashMap<>();
+ desugarInterfaceMethods(builder, IncludeAllResources, executorService, synthesizedClasses);
+
printPhase("Twr close resource utility class synthesis");
synthesizeTwrCloseResourceUtilityClass(builder);
synthesizeJava8UtilityClass(builder);
handleSynthesizedClassMapping(builder);
+
printPhase("Lambda merging finalization");
- finalizeLambdaMerging(application, feedback, builder, executorService);
+ finalizeLambdaMerging(application, feedback, builder, executorService, synthesizedClasses);
if (outliner != null) {
printPhase("Outlining");
timing.begin("IR conversion phase 2");
- if (outliner.selectMethodsForOutlining()) {
+ if (outliner.selectMethodsForOutlining(synthesizedClasses)) {
forEachSelectedOutliningMethod(
executorService,
(code, method) -> {
@@ -679,11 +692,12 @@
DexApplication application,
OptimizationFeedback directFeedback,
Builder<?> builder,
- ExecutorService executorService)
+ ExecutorService executorService,
+ Map<DexType, DexProgramClass> synthesizedClasses)
throws ExecutionException {
if (lambdaMerger != null) {
lambdaMerger.applyLambdaClassMapping(
- application, this, directFeedback, builder, executorService);
+ application, this, directFeedback, builder, executorService, synthesizedClasses);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 62cddc9..7c22bbf 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -156,7 +156,7 @@
checkInvokeDirect(method.method, invoke.asInvokeDirect());
}
GraphLenseLookupResult lenseLookup =
- graphLense.lookupMethod(invokedMethod, method, invoke.getType());
+ graphLense.lookupMethod(invokedMethod, method.method, invoke.getType());
DexMethod actualTarget = lenseLookup.getMethod();
Invoke.Type actualInvokeType = lenseLookup.getType();
if (actualInvokeType == Type.VIRTUAL) {
@@ -306,10 +306,11 @@
}
} else if (current.isMoveException()) {
MoveException moveException = current.asMoveException();
- if (moveException.hasOutValue()) {
- // Conservatively add the out-value to `newSSAValues` since the catch handler guards
- // may have been renamed as a result of class merging.
- newSSAValues.add(moveException.outValue());
+ DexType newExceptionType = graphLense.lookupType(moveException.getExceptionType());
+ if (newExceptionType != moveException.getExceptionType()) {
+ iterator.replaceCurrentInstruction(
+ new MoveException(
+ makeOutValue(moveException, code, newSSAValues), newExceptionType, options));
}
} else if (current.isNewArrayEmpty()) {
NewArrayEmpty newArrayEmpty = current.asNewArrayEmpty();
@@ -320,7 +321,7 @@
iterator.replaceCurrentInstruction(newNewArray);
}
} else if (current.isNewInstance()) {
- NewInstance newInstance= current.asNewInstance();
+ NewInstance newInstance = current.asNewInstance();
DexType newClazz = graphLense.lookupType(newInstance.clazz);
if (newClazz != newInstance.clazz) {
NewInstance newNewInstance = new NewInstance(
@@ -456,7 +457,7 @@
DexMethod invokedMethod = methodHandle.asMethod();
MethodHandleType oldType = methodHandle.type;
GraphLenseLookupResult lenseLookup =
- graphLense.lookupMethod(invokedMethod, context, oldType.toInvokeType());
+ graphLense.lookupMethod(invokedMethod, context.method, oldType.toInvokeType());
DexMethod rewrittenTarget = lenseLookup.getMethod();
DexMethod actualTarget;
MethodHandleType newType;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java
index a8616fb..b708ae9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.graph.AppInfo;
-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.graph.GraphLense;
@@ -13,14 +11,11 @@
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
-import java.util.Map;
class InterfaceMethodDesugaringLense extends NestedGraphLense {
- private final Map<DexEncodedMethod, DexEncodedMethod> methodsWithMovedCode;
InterfaceMethodDesugaringLense(
BiMap<DexMethod, DexMethod> methodMapping,
- Map<DexEncodedMethod, DexEncodedMethod> methodsWithMovedCode,
GraphLense previous, DexItemFactory factory) {
super(
ImmutableMap.of(),
@@ -30,12 +25,5 @@
methodMapping.inverse(),
previous,
factory);
- this.methodsWithMovedCode = methodsWithMovedCode;
- }
-
- @Override
- public DexEncodedMethod mapDexEncodedMethod(AppInfo appInfo, DexEncodedMethod original) {
- return super.mapDexEncodedMethod(
- appInfo, methodsWithMovedCode.getOrDefault(original, original));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 7a19fb8..4e91c62 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
@@ -18,6 +20,8 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -373,11 +377,14 @@
}
/**
- * Move static and default interface methods to companion classes,
- * add missing methods to forward to moved default methods implementation.
+ * Move static and default interface methods to companion classes, add missing methods to forward
+ * to moved default methods implementation.
*/
public void desugarInterfaceMethods(
- Builder<?> builder, Flavor flavour, ExecutorService executorService)
+ Builder<?> builder,
+ Flavor flavour,
+ ExecutorService executorService,
+ Map<DexType, DexProgramClass> synthesizedClasses)
throws ExecutionException {
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
@@ -387,12 +394,15 @@
// methods to companion class, copy default interface methods to companion classes,
// make original default methods abstract, remove bridge methods, create dispatch
// classes if needed.
- Map<DexType, DexProgramClass> synthesizedClasses = processInterfaces(builder, flavour);
-
- for (Map.Entry<DexType, DexProgramClass> entry : synthesizedClasses.entrySet()) {
+ for (Entry<DexType, DexProgramClass> entry : processInterfaces(builder, flavour).entrySet()) {
// Don't need to optimize synthesized class since all of its methods
// are just moved from interfaces and don't need to be re-processed.
- builder.addSynthesizedClass(entry.getValue(), isInMainDexList(entry.getKey()));
+ DexProgramClass synthesizedClass = entry.getValue();
+ builder.addSynthesizedClass(synthesizedClass, isInMainDexList(entry.getKey()));
+
+ if (synthesizedClasses != null) {
+ synthesizedClasses.put(synthesizedClass.type, synthesizedClass);
+ }
}
converter.optimizeSynthesizedMethods(synthesizedMethods, executorService);
@@ -414,23 +424,19 @@
}
private Map<DexType, DexProgramClass> processInterfaces(Builder<?> builder, Flavor flavour) {
+ NestedGraphLense.Builder graphLensBuilder = GraphLense.builder();
InterfaceProcessor processor = new InterfaceProcessor(this);
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, true)) {
- processor.process(clazz.asProgramClass());
+ processor.process(clazz.asProgramClass(), graphLensBuilder);
}
}
for (Entry<DexLibraryClass, Set<DexProgramClass>> entry : requiredDispatchClasses.entrySet()) {
synthesizedMethods.addAll(processor.process(entry.getKey(), entry.getValue()));
}
- if (converter.enableWholeProgramOptimizations &&
- (!processor.methodsWithMovedCode.isEmpty() || !processor.movedMethods.isEmpty())) {
- converter.appView.setGraphLense(
- new InterfaceMethodDesugaringLense(
- processor.movedMethods,
- processor.methodsWithMovedCode,
- converter.appView.graphLense(),
- factory));
+ if (converter.enableWholeProgramOptimizations) {
+ AppView<? extends AppInfoWithSubtyping> appView = converter.appView;
+ appView.setGraphLense(graphLensBuilder.build(appView.dexItemFactory(), appView.graphLense()));
}
return processor.syntheticClasses;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 6ba9b6a..f39e1df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -23,14 +23,13 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
@@ -52,14 +51,11 @@
// All created companion and dispatch classes indexed by interface type.
final Map<DexType, DexProgramClass> syntheticClasses = new IdentityHashMap<>();
- final BiMap<DexMethod, DexMethod> movedMethods = HashBiMap.create();
- final Map<DexEncodedMethod, DexEncodedMethod> methodsWithMovedCode = new IdentityHashMap<>();
-
InterfaceProcessor(InterfaceMethodRewriter rewriter) {
this.rewriter = rewriter;
}
- void process(DexProgramClass iface) {
+ void process(DexProgramClass iface, NestedGraphLense.Builder graphLensBuilder) {
assert iface.isInterface();
// The list of methods to be created in companion class.
@@ -101,8 +97,7 @@
DexEncodedMethod implMethod = new DexEncodedMethod(
companionMethod, newFlags, virtual.annotations, virtual.parameterAnnotationsList, code);
companionMethods.add(implMethod);
-
- methodsWithMovedCode.put(virtual, implMethod);
+ graphLensBuilder.move(virtual.method, implMethod.method);
}
// Remove bridge methods.
@@ -136,8 +131,7 @@
DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
companionMethods.add(new DexEncodedMethod(companionMethod, newFlags,
direct.annotations, direct.parameterAnnotationsList, direct.getCode()));
- movedMethods.put(oldMethod, companionMethod);
-
+ graphLensBuilder.move(oldMethod, companionMethod);
} else {
if (originalFlags.isPrivate()) {
assert !rewriter.factory.isClassConstructor(oldMethod)
@@ -160,8 +154,7 @@
companionMethods.add(new DexEncodedMethod(companionMethod,
newFlags, direct.annotations, direct.parameterAnnotationsList, code));
- movedMethods.put(oldMethod, companionMethod);
-
+ graphLensBuilder.move(oldMethod, companionMethod);
} else {
// Since there are no interface constructors at this point,
// this should only be class constructor.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriterGraphLense.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriterGraphLense.java
index 26f890e..d5ceeb1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriterGraphLense.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriterGraphLense.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar;
-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.graph.GraphLense;
@@ -28,13 +27,12 @@
}
@Override
- protected Type mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
+ protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
if (methodMap.get(originalMethod) == newMethod) {
assert type == Type.VIRTUAL || type == Type.DIRECT;
return Type.STATIC;
}
- return super.mapInvocationType(newMethod, originalMethod, context, type);
+ return super.mapInvocationType(newMethod, originalMethod, type);
}
}
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
index 49c740c..ca38f09 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
@@ -203,6 +203,10 @@
return hasSeenUpwardRecursive(method) || hasSeenDownwardRecursive(method);
}
+ public boolean hasSeenDirectly(Wrapper<DexMethod> method) {
+ return methodPool.contains(method);
+ }
+
private boolean hasSeenUpwardRecursive(Wrapper<DexMethod> method) {
return methodPool.contains(method)
|| (superType != null && superType.hasSeenUpwardRecursive(method))
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 7966d3a..87b2537 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1228,14 +1228,16 @@
}
}
- public boolean selectMethodsForOutlining() {
+ public boolean selectMethodsForOutlining(Map<DexType, DexProgramClass> synthesizedClasses) {
assert methodsSelectedForOutlining.size() == 0;
assert outlineSites.size() == 0;
for (List<DexEncodedMethod> outlineMethods : candidateMethodLists) {
if (outlineMethods.size() >= options.outline.threshold) {
for (DexEncodedMethod outlineMethod : outlineMethods) {
methodsSelectedForOutlining.add(
- converter.graphLense().mapDexEncodedMethod(appInfo, outlineMethod));
+ converter
+ .graphLense()
+ .mapDexEncodedMethod(outlineMethod, appInfo, synthesizedClasses));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 6a43c03..11e0e59 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -138,12 +138,27 @@
appView,
appView.appInfo().classes(),
clazz -> {
+ MethodPool methodPool = methodPoolCollection.get(clazz);
+
if (clazz.isInterface()) {
+ // Do not allow changing the prototype of methods that override an interface method.
+ // This achieved by faking that there is already a method with the given signature.
+ for (DexEncodedMethod virtualMethod : clazz.virtualMethods()) {
+ RewrittenPrototypeDescription prototypeChanges =
+ new RewrittenPrototypeDescription(
+ isAlwaysNull(virtualMethod.method.proto.returnType),
+ getRemovedArgumentsInfo(virtualMethod, ALLOW_ARGUMENT_REMOVAL));
+ if (!prototypeChanges.isEmpty()) {
+ DexMethod newMethod = getNewMethodSignature(virtualMethod, prototypeChanges);
+ Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
+ if (!methodPool.hasSeenDirectly(wrapper)) {
+ methodPool.seen(wrapper);
+ }
+ }
+ }
return;
}
- MethodPool methodPool = methodPoolCollection.get(clazz);
-
Map<DexEncodedMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
new IdentityHashMap<>();
for (DexEncodedMethod directMethod : clazz.directMethods()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 50d22e1..b988a89 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -194,7 +194,8 @@
IRConverter converter,
OptimizationFeedback feedback,
Builder<?> builder,
- ExecutorService executorService)
+ ExecutorService executorService,
+ Map<DexType, DexProgramClass> synthesizedClasses)
throws ExecutionException {
if (lambdas.isEmpty()) {
return;
@@ -218,14 +219,17 @@
// Add synthesized lambda group classes to the builder.
converter.optimizeSynthesizedClasses(lambdaGroupsClasses.values(), executorService);
+
for (Entry<LambdaGroup, DexProgramClass> entry : lambdaGroupsClasses.entrySet()) {
- builder.addSynthesizedClass(entry.getValue(),
- entry.getKey().shouldAddToMainDex(converter.appInfo));
+ DexProgramClass synthesizedClass = entry.getValue();
+ synthesizedClasses.put(synthesizedClass.type, synthesizedClass);
+ builder.addSynthesizedClass(
+ synthesizedClass, entry.getKey().shouldAddToMainDex(converter.appInfo));
}
// Rewrite lambda class references into lambda group class
// references inside methods from the processing queue.
- rewriteLambdaReferences(converter, feedback);
+ rewriteLambdaReferences(converter, synthesizedClasses, feedback);
this.strategyFactory = null;
}
@@ -300,7 +304,10 @@
}
}
- private void rewriteLambdaReferences(IRConverter converter, OptimizationFeedback feedback) {
+ private void rewriteLambdaReferences(
+ IRConverter converter,
+ Map<DexType, DexProgramClass> synthesizedClasses,
+ OptimizationFeedback feedback) {
List<DexEncodedMethod> methods =
methodsToReprocess
.stream()
@@ -308,7 +315,7 @@
.collect(Collectors.toList());
for (DexEncodedMethod method : methods) {
DexEncodedMethod mappedMethod =
- converter.graphLense().mapDexEncodedMethod(converter.appInfo, method);
+ converter.graphLense().mapDexEncodedMethod(method, converter.appInfo, synthesizedClasses);
converter.processMethod(mappedMethod, feedback,
x -> false, CallSiteInformation.empty(), Outliner::noProcessing);
assert mappedMethod.isProcessed();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLense.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLense.java
index 9bdd55e..96bb394 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLense.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.optimize.staticizer;
-import com.android.tools.r8.graph.AppInfo;
-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;
@@ -14,17 +12,14 @@
import com.android.tools.r8.ir.code.Invoke.Type;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableMap;
-import java.util.Map;
class ClassStaticizerGraphLense extends NestedGraphLense {
- private final Map<DexEncodedMethod, DexEncodedMethod> staticizedMethods;
ClassStaticizerGraphLense(
GraphLense previous,
DexItemFactory factory,
BiMap<DexField, DexField> fieldMapping,
- BiMap<DexMethod, DexMethod> methodMapping,
- Map<DexEncodedMethod, DexEncodedMethod> encodedMethodMapping) {
+ BiMap<DexMethod, DexMethod> methodMapping) {
super(ImmutableMap.of(),
methodMapping,
fieldMapping,
@@ -32,22 +27,14 @@
methodMapping.inverse(),
previous,
factory);
- staticizedMethods = encodedMethodMapping;
}
@Override
- protected Type mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod,
- DexEncodedMethod context, Type type) {
+ protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
if (methodMap.get(originalMethod) == newMethod) {
assert type == Type.VIRTUAL || type == Type.DIRECT;
return Type.STATIC;
}
- return super.mapInvocationType(newMethod, originalMethod, context, type);
- }
-
- @Override
- public DexEncodedMethod mapDexEncodedMethod(AppInfo appInfo, DexEncodedMethod original) {
- return super.mapDexEncodedMethod(appInfo, staticizedMethods.getOrDefault(original, original));
+ return super.mapInvocationType(newMethod, originalMethod, type);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 351ec8c..6dac0a0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -34,7 +34,6 @@
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
@@ -463,7 +462,6 @@
private Set<DexEncodedMethod> staticizeMethodSymbols() {
BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
- Map<DexEncodedMethod, DexEncodedMethod> encodedMethodMapping = new HashMap<>();
BiMap<DexField, DexField> fieldMapping = HashBiMap.create();
Set<DexEncodedMethod> staticizedMethods = Sets.newIdentityHashSet();
@@ -480,7 +478,6 @@
newDirectMethods.add(staticizedMethod);
staticizedMethods.add(staticizedMethod);
methodMapping.put(method.method, staticizedMethod.method);
- encodedMethodMapping.put(method, staticizedMethod);
}
}
candidateClass.setVirtualMethods(DexEncodedMethod.EMPTY_ARRAY);
@@ -506,8 +503,7 @@
classStaticizer.converter.graphLense(),
classStaticizer.factory,
fieldMapping,
- methodMapping,
- encodedMethodMapping));
+ methodMapping));
}
return staticizedMethods;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
index 98a0da4..5e19d48 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.optimize;
import com.android.tools.r8.graph.AppInfo;
-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.GraphLense;
@@ -47,11 +46,8 @@
return new Builder(appInfo);
}
-
@Override
- protected Type mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
- return super.mapVirtualInterfaceInvocationTypes(
- appInfo, newMethod, originalMethod, context, type);
+ protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
+ return super.mapVirtualInterfaceInvocationTypes(appInfo, newMethod, originalMethod, type);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
index e51b38d..f6d8fa3 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
@@ -15,6 +15,7 @@
import java.util.Set;
final class PublicizerLense extends NestedGraphLense {
+
private final AppView appView;
private final Set<DexMethod> publicizedMethods;
@@ -34,8 +35,7 @@
}
@Override
- public GraphLenseLookupResult lookupMethod(
- DexMethod method, DexEncodedMethod context, Type type) {
+ public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
method = previous.getMethod();
type = previous.getType();
@@ -46,7 +46,7 @@
return super.lookupMethod(method, context, type);
}
- private boolean publicizedMethodIsPresentOnHolder(DexMethod method, DexEncodedMethod context) {
+ private boolean publicizedMethodIsPresentOnHolder(DexMethod method, DexMethod context) {
GraphLenseLookupResult lookup =
appView.graphLense().lookupMethod(method, context, Type.VIRTUAL);
DexMethod signatureInCurrentWorld = lookup.getMethod();
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 45ca1af..fbe8968 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfo.ResolutionResult;
@@ -53,6 +52,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@@ -211,13 +211,13 @@
private final Set<DexProgramClass> mergeCandidates = new LinkedHashSet<>();
// Map from source class to target class.
- private final Map<DexType, DexType> mergedClasses = new HashMap<>();
+ private final Map<DexType, DexType> mergedClasses = new IdentityHashMap<>();
// Map from target class to the super classes that have been merged into the target class.
- private final Map<DexType, Set<DexType>> mergedClassesInverse = new HashMap<>();
+ private final Map<DexType, Set<DexType>> mergedClassesInverse = new IdentityHashMap<>();
// Set of types that must not be merged into their subtype.
- private final Set<DexType> pinnedTypes = new HashSet<>();
+ private final Set<DexType> pinnedTypes = Sets.newIdentityHashSet();
// The resulting graph lense that should be used after class merging.
private final VerticalClassMergerGraphLense.Builder renamedMembersLense;
@@ -423,7 +423,8 @@
return false;
}
DexClass targetClass = appInfo.definitionFor(clazz.type.getSingleSubtype());
- if (clazz.hasClassInitializer() && targetClass.hasClassInitializer()) {
+ if ((clazz.hasClassInitializer() && targetClass.hasClassInitializer())
+ || targetClass.classInitializationMayHaveSideEffects(appInfo, type -> type == clazz.type)) {
// TODO(herhut): Handle class initializers.
if (Log.ENABLED) {
AbortReason.STATIC_INITIALIZERS.printLogMessageForClass(clazz);
@@ -685,7 +686,7 @@
if (Log.ENABLED) {
Log.debug(getClass(), "Merged %d classes.", mergedClasses.size());
}
- return renamedMembersLense.build(graphLense, mergedClasses, synthesizedBridges, appInfo);
+ return renamedMembersLense.build(graphLense, mergedClasses, appInfo);
}
private boolean methodResolutionMayChange(DexClass source, DexClass target) {
@@ -1432,42 +1433,19 @@
for (DexProgramClass clazz : appInfo.classes()) {
clazz.setDirectMethods(substituteTypesIn(clazz.directMethods()));
clazz.setVirtualMethods(substituteTypesIn(clazz.virtualMethods()));
- clazz.setVirtualMethods(removeDupes(clazz.virtualMethods()));
clazz.setStaticFields(substituteTypesIn(clazz.staticFields()));
clazz.setInstanceFields(substituteTypesIn(clazz.instanceFields()));
}
+ for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
+ synthesizedBridge.updateMethodSignatures(this::fixupMethod);
+ }
// Record type renamings so check-cast and instance-of checks are also fixed.
for (DexType type : mergedClasses.keySet()) {
- DexType fixed = fixupType(type);
- lense.map(type, fixed);
+ lense.map(type, fixupType(type));
}
return lense.build(application.dexItemFactory, graphLense);
}
- private DexEncodedMethod[] removeDupes(DexEncodedMethod[] methods) {
- if (methods == null) {
- return null;
- }
- Map<DexMethod, DexEncodedMethod> filtered = new IdentityHashMap<>();
- for (DexEncodedMethod method : methods) {
- DexEncodedMethod previous = filtered.put(method.method, method);
- if (previous != null) {
- if (!previous.accessFlags.isBridge()) {
- if (!method.accessFlags.isBridge()) {
- throw new CompilationError(
- "Class merging produced invalid result on: " + previous.toSourceString());
- } else {
- filtered.put(previous.method, previous);
- }
- }
- }
- }
- if (filtered.size() == methods.length) {
- return methods;
- }
- return filtered.values().toArray(DexEncodedMethod.EMPTY_ARRAY);
- }
-
private DexEncodedMethod[] substituteTypesIn(DexEncodedMethod[] methods) {
if (methods == null) {
return null;
@@ -1475,12 +1453,9 @@
for (int i = 0; i < methods.length; i++) {
DexEncodedMethod encodedMethod = methods[i];
DexMethod method = encodedMethod.method;
- DexProto newProto = getUpdatedProto(method.proto);
- DexType newHolder = fixupType(method.holder);
- DexMethod newMethod = application.dexItemFactory.createMethod(newHolder, newProto,
- method.name);
- if (newMethod != encodedMethod.method) {
- lense.move(encodedMethod.method, newMethod);
+ DexMethod newMethod = fixupMethod(method);
+ if (newMethod != method) {
+ lense.move(method, newMethod);
methods[i] = encodedMethod.toTypeSubstitutedMethod(newMethod);
}
}
@@ -1505,7 +1480,12 @@
return fields;
}
- private DexProto getUpdatedProto(DexProto proto) {
+ private DexMethod fixupMethod(DexMethod method) {
+ return application.dexItemFactory.createMethod(
+ fixupType(method.holder), fixupProto(method.proto), method.name);
+ }
+
+ private DexProto fixupProto(DexProto proto) {
DexProto result = protoFixupCache.get(proto);
if (result == null) {
DexType returnType = fixupType(proto.returnType);
@@ -1522,12 +1502,13 @@
DexType fixed = fixupType(base);
if (base == fixed) {
return type;
- } else {
- return type.replaceBaseType(fixed, application.dexItemFactory);
}
+ return type.replaceBaseType(fixed, application.dexItemFactory);
}
- while (mergedClasses.containsKey(type)) {
- type = mergedClasses.get(type);
+ if (type.isClassType()) {
+ while (mergedClasses.containsKey(type)) {
+ type = mergedClasses.get(type);
+ }
}
return type;
}
@@ -1680,6 +1661,11 @@
}
@Override
+ public DexType getOriginalType(DexType type) {
+ throw new Unreachable();
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
throw new Unreachable();
}
@@ -1705,8 +1691,7 @@
}
@Override
- public GraphLenseLookupResult lookupMethod(
- DexMethod method, DexEncodedMethod context, Type type) {
+ public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
// First look up the method using the existing graph lense (for example, the type will have
// changed if the method was publicized by ClassAndMemberPublicizer).
GraphLenseLookupResult lookup = graphLense.lookupMethod(method, context, type);
@@ -1750,7 +1735,7 @@
public static class IllegalAccessDetector extends UseRegistry {
private boolean foundIllegalAccess = false;
- private DexEncodedMethod context = null;
+ private DexMethod context = null;
private final AppView<? extends AppInfo> appView;
private final DexClass source;
@@ -1766,7 +1751,7 @@
}
public void setContext(DexEncodedMethod context) {
- this.context = context;
+ this.context = context.method;
}
private boolean checkFieldReference(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
index 20a05ab..6576433 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.graph.AppInfo;
-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;
@@ -14,17 +13,14 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.shaking.VerticalClassMerger.SynthesizedBridgeCode;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.HashMap;
import java.util.IdentityHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
// This graph lense is instantiated during vertical class merging. The graph lense is context
// sensitive in the enclosing class of a given invoke *and* the type of the invoke (e.g., invoke-
@@ -55,7 +51,7 @@
private final Map<DexType, Map<DexMethod, GraphLenseLookupResult>>
contextualVirtualToDirectMethodMaps;
- private final Set<DexMethod> mergedMethods;
+ private Set<DexMethod> mergedMethods;
private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges;
public VerticalClassMergerGraphLense(
@@ -83,19 +79,29 @@
}
@Override
+ public DexType getOriginalType(DexType type) {
+ return previousLense.getOriginalType(type);
+ }
+
+ @Override
public DexMethod getOriginalMethodSignature(DexMethod method) {
return super.getOriginalMethodSignature(
originalMethodSignaturesForBridges.getOrDefault(method, method));
}
@Override
- public GraphLenseLookupResult lookupMethod(
- DexMethod method, DexEncodedMethod context, Type type) {
+ public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
assert isContextFreeForMethod(method) || (context != null && type != null);
- GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
- if (previous.getType() == Type.SUPER && !mergedMethods.contains(context.method)) {
+ DexMethod previousContext =
+ originalMethodSignaturesForBridges.containsKey(context)
+ ? originalMethodSignaturesForBridges.get(context)
+ : originalMethodSignatures != null
+ ? originalMethodSignatures.getOrDefault(context, context)
+ : context;
+ GraphLenseLookupResult previous = previousLense.lookupMethod(method, previousContext, type);
+ if (previous.getType() == Type.SUPER && !mergedMethods.contains(context)) {
Map<DexMethod, GraphLenseLookupResult> virtualToDirectMethodMap =
- contextualVirtualToDirectMethodMaps.get(context.method.holder);
+ contextualVirtualToDirectMethodMaps.get(context.holder);
if (virtualToDirectMethodMap != null) {
GraphLenseLookupResult lookup = virtualToDirectMethodMap.get(previous.getMethod());
if (lookup != null) {
@@ -113,10 +119,8 @@
}
@Override
- protected Type mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
- return super.mapVirtualInterfaceInvocationTypes(
- appInfo, newMethod, originalMethod, context, type);
+ protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
+ return super.mapVirtualInterfaceInvocationTypes(appInfo, newMethod, originalMethod, type);
}
@Override
@@ -170,7 +174,6 @@
public GraphLense build(
GraphLense previousLense,
Map<DexType, DexType> mergedClasses,
- List<SynthesizedBridgeCode> synthesizedBridges,
AppInfo appInfo) {
if (fieldMap.isEmpty()
&& methodMap.isEmpty()
@@ -179,14 +182,6 @@
}
Map<DexProto, DexProto> cache = new HashMap<>();
BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
- // Update all synthesized bridges.
- Function<DexMethod, DexMethod> synthesizedBridgeTransformer =
- method ->
- getMethodSignatureAfterClassMerging(
- method, mergedClasses, appInfo.dexItemFactory, cache);
- for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
- synthesizedBridge.updateMethodSignatures(synthesizedBridgeTransformer);
- }
// Build new graph lense.
return new VerticalClassMergerGraphLense(
appInfo,
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index ec12f82..c9be1b8 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.naming.ClassNameMapper;
@@ -150,16 +151,17 @@
// At this point we don't know if we really need to add this class to the builder.
// It depends on whether any methods/fields are renamed or some methods contain positions.
// Create a supplier which creates a new, cached ClassNaming.Builder on-demand.
+ DexType originalType = graphLense.getOriginalType(clazz.type);
DexString renamedClassName = namingLens.lookupDescriptor(clazz.getType());
Supplier<ClassNaming.Builder> onDemandClassNamingBuilder =
Suppliers.memoize(
() ->
classNameMapperBuilder.classNamingBuilder(
DescriptorUtils.descriptorToJavaType(renamedClassName.toString()),
- clazz.toString()));
+ originalType.toSourceString()));
// If the class is renamed add it to the classNamingBuilder.
- addClassToClassNaming(clazz, renamedClassName, onDemandClassNamingBuilder);
+ addClassToClassNaming(originalType, renamedClassName, onDemandClassNamingBuilder);
// First transfer renamed fields to classNamingBuilder.
addFieldsToClassNaming(graphLense, namingLens, clazz, onDemandClassNamingBuilder);
@@ -306,10 +308,12 @@
}
@SuppressWarnings("ReturnValueIgnored")
- private static void addClassToClassNaming(DexProgramClass clazz, DexString renamedClassName,
+ private static void addClassToClassNaming(
+ DexType originalType,
+ DexString renamedClassName,
Supplier<Builder> onDemandClassNamingBuilder) {
// We do know we need to create a ClassNaming.Builder if the class itself had been renamed.
- if (!clazz.toString().equals(renamedClassName.toString())) {
+ if (originalType.descriptor != renamedClassName) {
// Not using return value, it's registered in classNameMapperBuilder
onDemandClassNamingBuilder.get();
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/StaticInitializerTest.java b/src/test/java/com/android/tools/r8/classmerging/StaticInitializerTest.java
new file mode 100644
index 0000000..ce89fe1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/StaticInitializerTest.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StaticInitializerTest extends TestBase {
+
+ private final Backend backend;
+
+ @Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ public StaticInitializerTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("In A.m()", "In B.<clinit>()", "In B.m()");
+
+ if (backend == Backend.CF) {
+ testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+ }
+
+ testForR8(backend)
+ .addInnerClasses(StaticInitializerTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ A.m();
+ B.m();
+ }
+ }
+
+ // Cannot be merged into B because that would change the semantics due to <clinit>.
+ static class A {
+
+ @NeverInline
+ public static void m() {
+ System.out.println("In A.m()");
+ }
+ }
+
+ static class B extends A {
+
+ static {
+ System.out.println("In B.<clinit>()");
+ }
+
+ @NeverInline
+ public static void m() {
+ System.out.println("In B.m()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
index 2284fa5..c4151d1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
@@ -86,16 +86,13 @@
private void runR8(Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
throws IOException, ExecutionException, CompilationFailedException {
- ToolHelper.runR8(
- R8Command.builder()
- .setOutput(Paths.get(temp.getRoot().getCanonicalPath()), OutputMode.DexIndexed)
+ inspector =
+ testForR8(Backend.DEX)
.addProgramFiles(EXAMPLE_JAR)
- .addProguardConfigurationFiles(proguardConfig)
- .setDisableMinification(true)
- .build(),
- optionsConsumer);
- inspector = new CodeInspector(
- Paths.get(temp.getRoot().getCanonicalPath()).resolve("classes.dex"));
+ .addKeepRuleFiles(proguardConfig)
+ .addOptionsModification(optionsConsumer)
+ .compile()
+ .inspector();
}
private CodeInspector inspector;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InterfaceMethodTest.java
new file mode 100644
index 0000000..b609c1e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InterfaceMethodTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.uninstantiatedtypes;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InterfaceMethodTest extends TestBase {
+
+ private final Backend backend;
+
+ @Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ public InterfaceMethodTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("In A.m()", "In B.m()");
+
+ if (backend == Backend.CF) {
+ testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+ }
+
+ CodeInspector inspector =
+ testForR8(backend)
+ .addInnerClasses(InterfaceMethodTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput)
+ .inspector();
+
+ ClassSubject interfaceSubject = inspector.clazz(I.class);
+ assertThat(interfaceSubject, isPresent());
+ assertThat(interfaceSubject.method(Uninstantiated.class.getTypeName(), "m"), isPresent());
+
+ for (Class<?> clazz : ImmutableList.of(A.class, B.class)) {
+ ClassSubject classSubject = inspector.clazz(clazz);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject.method(Uninstantiated.class.getTypeName(), "m"), isPresent());
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ test(new A());
+ test(new B());
+ }
+
+ @NeverInline
+ private static void test(I obj) {
+ obj.m();
+ }
+ }
+
+ @NeverMerge
+ interface I {
+
+ Uninstantiated m();
+ }
+
+ static class A implements I {
+
+ @NeverInline
+ @Override
+ public Uninstantiated m() {
+ System.out.println("In A.m()");
+ return null;
+ }
+ }
+
+ // The purpose of this class is merely to avoid that the invoke-interface instruction in
+ // TestClass.test() gets devirtualized to an invoke-virtual instruction. Otherwise the method
+ // I.m() would not be present in the output.
+ static class B implements I {
+
+ @Override
+ public Uninstantiated m() {
+ System.out.println("In B.m()");
+ return null;
+ }
+ }
+
+ static class Uninstantiated {}
+}
diff --git a/src/test/java/com/android/tools/r8/proguard/printmapping/PrintMappingTest.java b/src/test/java/com/android/tools/r8/proguard/printmapping/PrintMappingTest.java
index e9bf581..378cf0e 100644
--- a/src/test/java/com/android/tools/r8/proguard/printmapping/PrintMappingTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/printmapping/PrintMappingTest.java
@@ -27,14 +27,11 @@
private void test(Path mapping) throws Exception {
testForR8(Backend.DEX)
.addInnerClasses(PrintMappingTest.class)
- .addKeepMainRule(TestClass.class)
+ .addKeepRules("-keep,allowobfuscation class " + TestClass.class.getTypeName())
.addKeepRules("-printmapping " + mapping)
.compile();
assertTrue(mapping.toFile().exists());
}
- static class TestClass {
-
- public static void main(String[] args) {}
- }
+ static class TestClass {}
}
diff --git a/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java b/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
index 1c28054..5b7a11e 100644
--- a/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
+++ b/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.DataResourceConsumer;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.R8Command;
+import com.android.tools.r8.StringResource;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.ClassNameMapper;
@@ -65,10 +66,15 @@
// Return the package name in the app for this package.
private String pathForThisPackage(AndroidApp app) throws Exception {
- ClassNameMapper mapper =
- ClassNameMapper.mapperFromString(app.getProguardMapOutputData().getString());
- String x = mapper.getObfuscatedToOriginalMapping().inverse.get(Main.class.getCanonicalName());
- return x.substring(0, x.lastIndexOf('.')).replace('.', '/');
+ String name;
+ if (app.getProguardMapOutputData() != null) {
+ ClassNameMapper mapper =
+ ClassNameMapper.mapperFromString(app.getProguardMapOutputData().getString());
+ name = mapper.getObfuscatedToOriginalMapping().inverse.get(Main.class.getCanonicalName());
+ } else {
+ name = Main.class.getTypeName();
+ }
+ return name.substring(0, name.lastIndexOf('.')).replace('.', '/');
}
private void checkResourceNames(Set<String> resourceNames, String expectedPackageName) {
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 b302615..0b246a5 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -260,9 +260,8 @@
}
if (inspection != null) {
- CodeInspector inspector = new CodeInspector(out,
- minify.isMinify() ? proguardMap.toString()
- : null);
+ CodeInspector inspector =
+ new CodeInspector(out, minify.isMinify() ? proguardMap.toString() : null);
inspection.accept(inspector);
}
}
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 f65c1d4..3833645 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
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.examples;
-import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.shaking.TreeShakingTest;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
new file mode 100644
index 0000000..07d6d22
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.ifrule.interfacemethoddesugaring;
+
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+
+public class IfRuleWithInterfaceMethodDesugaringTest extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput =
+ StringUtils.lines("In Interface.staticMethod()", "In Interface.virtualMethod()");
+
+ testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .addInnerClasses(IfRuleWithInterfaceMethodDesugaringTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(
+ "-if class " + Interface.class.getTypeName() + " {",
+ " !public static void staticMethod();",
+ "}",
+ "-keep class " + Unused1.class.getTypeName(),
+ "-if class " + Interface.class.getTypeName() + " {",
+ " !public !static void virtualMethod();",
+ "}",
+ "-keep class " + Unused2.class.getTypeName())
+ .enableInliningAnnotations()
+ .enableClassInliningAnnotations()
+ .enableMergeAnnotations()
+ .setMinApi(AndroidApiLevel.M)
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput)
+ .inspector();
+
+ ClassSubject classSubject =
+ inspector.clazz(Interface.class.getTypeName() + COMPANION_CLASS_NAME_SUFFIX);
+ assertThat(classSubject, isPresent());
+ assertEquals(2, classSubject.allMethods().size());
+
+ MethodSubject staticMethodSubject = classSubject.uniqueMethodWithName("staticMethod");
+ assertThat(staticMethodSubject, allOf(isPresent(), isPublic(), isStatic()));
+
+ // TODO(b/120764902): MethodSubject.getOriginalName() not working in presence of desugaring.
+ MethodSubject virtualMethodSubject =
+ classSubject.allMethods().stream()
+ .filter(subject -> subject != staticMethodSubject)
+ .findFirst()
+ .get();
+ assertThat(virtualMethodSubject, allOf(isPresent(), isPublic(), isStatic()));
+
+ // TODO(b/122875545): The Unused class should be present due to the -if rule.
+ assertThat(inspector.clazz(Unused1.class), not(isPresent()));
+ assertThat(inspector.clazz(Unused2.class), not(isPresent()));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Interface.staticMethod();
+ new InterfaceImpl().virtualMethod();
+ }
+ }
+
+ @NeverClassInline
+ @NeverMerge
+ interface Interface {
+
+ @NeverInline
+ static void staticMethod() {
+ System.out.println("In Interface.staticMethod()");
+ }
+
+ @NeverInline
+ default void virtualMethod() {
+ System.out.println("In Interface.virtualMethod()");
+ }
+ }
+
+ @NeverClassInline
+ static class InterfaceImpl implements Interface {}
+
+ static class Unused1 {}
+
+ static class Unused2 {}
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 1f520ff..d870316 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -45,6 +45,7 @@
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.lang.reflect.Method;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
@@ -82,13 +83,14 @@
public CodeInspector(
List<Path> files, String mappingFile, Consumer<InternalOptions> optionsConsumer)
throws IOException, ExecutionException {
- if (mappingFile != null) {
- this.mapping = ClassNameMapper.mapperFromFile(Paths.get(mappingFile));
- BiMapContainer<String, String> nameMapping = this.mapping.getObfuscatedToOriginalMapping();
+ Path mappingPath = mappingFile != null ? Paths.get(mappingFile) : null;
+ if (mappingPath != null && Files.exists(mappingPath)) {
+ mapping = ClassNameMapper.mapperFromFile(mappingPath);
+ BiMapContainer<String, String> nameMapping = mapping.getObfuscatedToOriginalMapping();
obfuscatedToOriginalMapping = nameMapping.original;
originalToObfuscatedMapping = nameMapping.inverse;
} else {
- this.mapping = null;
+ mapping = null;
originalToObfuscatedMapping = null;
obfuscatedToOriginalMapping = null;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 1167d8f..ed4a67f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -97,7 +97,7 @@
public MethodSubject uniqueMethodWithName(String name) {
MethodSubject methodSubject = null;
for (FoundMethodSubject candidate : allMethods()) {
- if (candidate.getOriginalName().equals(name)) {
+ if (candidate.getOriginalName(false).equals(name)) {
assert methodSubject == null;
methodSubject = candidate;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index a9abba1..d407e80 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -124,6 +124,28 @@
};
}
+ public static Matcher<MemberSubject> isStatic() {
+ return new TypeSafeMatcher<MemberSubject>() {
+ @Override
+ public boolean matchesSafely(final MemberSubject subject) {
+ return subject.isPresent() && subject.isStatic();
+ }
+
+ @Override
+ public void describeTo(final Description description) {
+ description.appendText(" present");
+ }
+
+ @Override
+ public void describeMismatchSafely(final MemberSubject subject, Description description) {
+ description
+ .appendText(type(subject) + " ")
+ .appendValue(name(subject))
+ .appendText(" was not");
+ }
+ };
+ }
+
public static Matcher<ClassSubject> hasDefaultConstructor() {
return new TypeSafeMatcher<ClassSubject>() {
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
index a95b6e6..bc933b1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
@@ -25,8 +25,21 @@
public abstract Signature getFinalSignature();
public String getOriginalName() {
+ return getOriginalName(true);
+ }
+ public String getOriginalName(boolean qualified) {
Signature originalSignature = getOriginalSignature();
- return originalSignature == null ? null : originalSignature.name;
+ if (originalSignature != null) {
+ String name = originalSignature.name;
+ if (!qualified) {
+ int index = name.lastIndexOf(".");
+ if (index >= 0) {
+ return name.substring(index + 1);
+ }
+ }
+ return name;
+ }
+ return null;
}
public String getFinalName() {