Merge commit '6912a3bf054937227890bc9e52d6c5c7aaf93280' into dev-release
diff --git a/build.gradle b/build.gradle
index 1a9edec..bce1569 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,7 +15,7 @@
buildscript {
repositories {
maven {
- url 'http://storage.googleapis.com/r8-deps/maven_mirror/'
+ url 'https://storage.googleapis.com/r8-deps/maven_mirror/'
}
mavenCentral()
gradlePluginPortal()
@@ -54,7 +54,7 @@
repositories {
maven {
- url 'http://storage.googleapis.com/r8-deps/maven_mirror/'
+ url 'https://storage.googleapis.com/r8-deps/maven_mirror/'
}
google()
mavenCentral()
@@ -1822,7 +1822,10 @@
result.exception.printStackTrace(processIn)
processIn.flush()
processIn.close()
- process.waitFor()
+ if (process.waitFor() != 0) {
+ out.append("ERROR DURING RETRACING\n")
+ out.append(err.toString())
+ }
out.append("\n\n--------------------------------------\n")
out.append("OBFUSCATED STACKTRACE\n")
out.append("--------------------------------------\n")
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 9c23fd1..63074c4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -85,7 +85,7 @@
this.options = options;
this.rewritePrefix = mapper;
- if (enableWholeProgramOptimizations() && options.enablePropagationOfDynamicTypesAtCallSites) {
+ if (enableWholeProgramOptimizations() && options.isCallSiteOptimizationEnabled()) {
this.callSiteOptimizationInfoPropagator =
new CallSiteOptimizationInfoPropagator(withLiveness());
} else {
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index e2adfbb..2ee2c0c 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -136,7 +136,12 @@
ps.println("# Annotations:");
for (DexAnnotation annotation : annotations.annotations) {
ps.print("# ");
- ps.println(annotation);
+ if (annotation.annotation.type
+ == application.dexItemFactory.createType("Lkotlin/Metadata;")) {
+ ps.println("<kotlin metadata>");
+ } else {
+ ps.println(annotation);
+ }
}
}
}
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 d86a90c..d540b18 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -676,14 +676,6 @@
return this;
}
- public boolean isProgramClass() {
- return false;
- }
-
- public DexProgramClass asProgramClass() {
- return null;
- }
-
public boolean isClasspathClass() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinition.java b/src/main/java/com/android/tools/r8/graph/DexDefinition.java
index 82dfc12..df8a65f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinition.java
@@ -20,6 +20,14 @@
return null;
}
+ public boolean isProgramClass() {
+ return false;
+ }
+
+ public DexProgramClass asProgramClass() {
+ return null;
+ }
+
public boolean isDexEncodedField() {
return false;
}
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 7b529da..7af203c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -47,7 +47,6 @@
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
@@ -1243,13 +1242,6 @@
}
}
- public void copyMetadata(DexEncodedMethod from, OptimizationFeedback feedback) {
- if (from.getOptimizationInfo().useIdentifierNameString()) {
- feedback.markUseIdentifierNameString(this);
- }
- copyMetadata(from);
- }
-
private static Builder syntheticBuilder(DexEncodedMethod from) {
return new Builder(from, 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 7800639..67a0178 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
@@ -105,7 +105,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -418,13 +417,12 @@
private void desugarInterfaceMethods(
Builder<?> builder,
- OptimizationFeedback feedback,
Flavor includeAllResources,
ExecutorService executorService)
throws ExecutionException {
if (interfaceMethodRewriter != null) {
interfaceMethodRewriter.desugarInterfaceMethods(
- builder, feedback, includeAllResources, executorService);
+ builder, includeAllResources, executorService);
}
}
@@ -460,7 +458,7 @@
desugarNestBasedAccess(builder, executor);
synthesizeLambdaClasses(builder, executor);
- desugarInterfaceMethods(builder, simpleOptimizationFeedback, ExcludeDexResources, executor);
+ desugarInterfaceMethods(builder, ExcludeDexResources, executor);
synthesizeTwrCloseResourceUtilityClass(builder, executor);
synthesizeJava8UtilityClass(builder, executor);
processCovariantReturnTypeAnnotations(builder);
@@ -707,7 +705,7 @@
synthesizeLambdaClasses(builder, executorService);
printPhase("Interface method desugaring");
- desugarInterfaceMethods(builder, feedback, IncludeAllResources, executorService);
+ desugarInterfaceMethods(builder, IncludeAllResources, executorService);
feedback.updateVisibleOptimizationInfo();
printPhase("Twr close resource utility class synthesis");
@@ -1100,7 +1098,10 @@
codeRewriter.simplifyDebugLocals(code);
}
- if (!method.isProcessed()) {
+ if (method.isProcessed()) {
+ assert !appView.enableWholeProgramOptimizations()
+ || !appView.appInfo().withLiveness().neverReprocess.contains(method.method);
+ } else {
if (lensCodeRewriter != null) {
timing.begin("Lens rewrite");
lensCodeRewriter.rewrite(code, method);
@@ -1535,12 +1536,6 @@
timing.end();
}
- // IR processing should not need to handle identifier name strings, so after the first round
- // there should be no information for that.
- if (methodProcessor.isPrimary()) {
- assert !method.getOptimizationInfo().useIdentifierNameString();
- }
-
printMethod(code, "Optimized IR (SSA)", previous);
timing.begin("Finalize IR");
finalizeIR(method, code, feedback, timing);
@@ -1548,17 +1543,6 @@
return timing;
}
- public void collectIdentifierNameStringUse(
- DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
- Iterator<Instruction> iterator = code.instructionIterator();
- while (iterator.hasNext()) {
- if (iterator.next().isDexItemBasedConstString()) {
- feedback.markUseIdentifierNameString(method);
- break;
- }
- }
- }
-
// Compute optimization info summary for the current method unless it is pinned
// (in that case we should not be making any assumptions about the behavior of the method).
public void collectOptimizationInfo(
@@ -1575,7 +1559,6 @@
public void finalizeIR(
DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
- collectIdentifierNameStringUse(method, code, feedback);
code.traceBlocks();
if (options.isGeneratingClassFiles()) {
finalizeToCf(method, code, feedback);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 7d4a203..a15cbe4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -51,8 +51,6 @@
void markAsPropagated(DexEncodedMethod method);
- void markUseIdentifierNameString(DexEncodedMethod method);
-
void markCheckNullReceiverBeforeAnySideEffect(DexEncodedMethod method, boolean mark);
void markTriggerClassInitBeforeAnySideEffect(DexEncodedMethod method, boolean mark);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 7e0c9d5..96c93a9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -21,9 +21,11 @@
import java.util.Deque;
import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
class PostMethodProcessor implements MethodProcessor {
@@ -87,6 +89,13 @@
PostMethodProcessor build(
AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
throws ExecutionException {
+ if (!appView.appInfo().reprocess.isEmpty()) {
+ put(
+ appView.appInfo().reprocess.stream()
+ .map(appView::definitionFor)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet()));
+ }
if (methodsMap.keySet().isEmpty()) {
// Nothing to revisit.
return null;
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 21b182e..6a79659 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
@@ -38,7 +38,6 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLense;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.position.MethodPosition;
@@ -914,7 +913,6 @@
*/
public void desugarInterfaceMethods(
Builder<?> builder,
- OptimizationFeedback feedback,
Flavor flavour,
ExecutorService executorService)
throws ExecutionException {
@@ -933,7 +931,7 @@
// classes if needed.
AppInfo appInfo = appView.appInfo();
for (Entry<DexType, DexProgramClass> entry :
- processInterfaces(builder, feedback, flavour).entrySet()) {
+ 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.
DexProgramClass synthesizedClass = entry.getValue();
@@ -964,9 +962,9 @@
}
private Map<DexType, DexProgramClass> processInterfaces(
- Builder<?> builder, OptimizationFeedback feedback, Flavor flavour) {
+ Builder<?> builder, Flavor flavour) {
NestedGraphLense.Builder graphLensBuilder = InterfaceProcessorNestedGraphLense.builder();
- InterfaceProcessor processor = new InterfaceProcessor(appView, this, feedback);
+ InterfaceProcessor processor = new InterfaceProcessor(appView, this);
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, true)) {
processor.process(clazz, graphLensBuilder);
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 ef9b9c9..04ce705 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
@@ -31,7 +31,6 @@
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.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
@@ -57,16 +56,14 @@
private final AppView<?> appView;
private final InterfaceMethodRewriter rewriter;
- private final OptimizationFeedback feedback;
// All created companion and dispatch classes indexed by interface type.
final Map<DexType, DexProgramClass> syntheticClasses = new IdentityHashMap<>();
InterfaceProcessor(
- AppView<?> appView, InterfaceMethodRewriter rewriter, OptimizationFeedback feedback) {
+ AppView<?> appView, InterfaceMethodRewriter rewriter) {
this.appView = appView;
this.rewriter = rewriter;
- this.feedback = feedback;
}
void process(DexProgramClass iface, NestedGraphLense.Builder graphLensBuilder) {
@@ -99,7 +96,7 @@
code, companionMethod.getArity(), appView);
DexEncodedMethod implMethod = new DexEncodedMethod(
companionMethod, newFlags, virtual.annotations, virtual.parameterAnnotationsList, code, true);
- implMethod.copyMetadata(virtual, feedback);
+ implMethod.copyMetadata(virtual);
virtual.setDefaultInterfaceMethodImplementation(implMethod);
companionMethods.add(implMethod);
graphLensBuilder.move(virtual.method, implMethod.method);
@@ -141,7 +138,7 @@
direct.parameterAnnotationsList,
direct.getCode(),
true);
- implMethod.copyMetadata(direct, feedback);
+ implMethod.copyMetadata(direct);
companionMethods.add(implMethod);
graphLensBuilder.move(oldMethod, companionMethod);
} else {
@@ -168,7 +165,7 @@
direct.parameterAnnotationsList,
code,
true);
- implMethod.copyMetadata(direct, feedback);
+ implMethod.copyMetadata(direct);
companionMethods.add(implMethod);
graphLensBuilder.move(oldMethod, companionMethod);
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index 00c797d..627aec5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -155,6 +155,9 @@
// can avoid recording arguments for the same method accidentally.
private void recordArgumentsIfNecessary(DexEncodedMethod target, List<Value> inValues) {
assert !target.isObsolete();
+ if (appView.appInfo().neverReprocess.contains(target.method)) {
+ return;
+ }
if (target.getCallSiteOptimizationInfo().isTop()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index eff0d07..a63cb59 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -11,8 +11,10 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstNumber;
@@ -85,7 +87,8 @@
}
public void canonicalize(AppView<?> appView, IRCode code) {
- DexType context = code.method.method.holder;
+ DexEncodedMethod method = code.method;
+ DexType context = method.method.holder;
Object2ObjectLinkedOpenCustomHashMap<Instruction, List<Value>> valuesDefinedByConstant =
new Object2ObjectLinkedOpenCustomHashMap<>(
new Strategy<Instruction>() {
@@ -145,6 +148,13 @@
if (!abstractValue.isSingleFieldValue()) {
continue;
}
+ SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
+ if (method.isClassInitializer()
+ && method.method.holder == singleFieldValue.getField().holder) {
+ // Avoid that canonicalization inserts a read before the unique write in the class
+ // initializer, as that would change the program behavior.
+ continue;
+ }
if (current.instructionMayHaveSideEffects(appView, context)) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 06a94a6..36ccecf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -28,7 +28,6 @@
static AbstractValue UNKNOWN_ABSTRACT_RETURN_VALUE = UnknownValue.getInstance();
static TypeLatticeElement UNKNOWN_TYPE = null;
static ClassTypeLatticeElement UNKNOWN_CLASS_TYPE = null;
- static boolean DOES_NOT_USE_IDENTIFIER_NAME_STRING = false;
static boolean UNKNOWN_CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT = false;
static boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
static ClassInlinerEligibilityInfo UNKNOWN_CLASS_INLINER_ELIGIBILITY = null;
@@ -149,11 +148,6 @@
}
@Override
- public boolean useIdentifierNameString() {
- return DOES_NOT_USE_IDENTIFIER_NAME_STRING;
- }
-
- @Override
public boolean forceInline() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index fd8078d..46749fd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -68,8 +68,6 @@
boolean neverInline();
- boolean useIdentifierNameString();
-
boolean checksNullReceiverBeforeAnySideEffect();
boolean triggersClassInitBeforeAnySideEffect();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 330eba6..27f8be0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -210,11 +210,6 @@
}
@Override
- public synchronized void markUseIdentifierNameString(DexEncodedMethod method) {
- getMethodOptimizationInfoForUpdating(method).markUseIdentifierNameString();
- }
-
- @Override
public synchronized void markCheckNullReceiverBeforeAnySideEffect(
DexEncodedMethod method, boolean mark) {
getMethodOptimizationInfoForUpdating(method).markCheckNullReceiverBeforeAnySideEffect(mark);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 2495e58..8d6e8c4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -99,9 +99,6 @@
public void markProcessed(DexEncodedMethod method, ConstraintWithTarget state) {}
@Override
- public void markUseIdentifierNameString(DexEncodedMethod method) {}
-
- @Override
public void markCheckNullReceiverBeforeAnySideEffect(DexEncodedMethod method, boolean mark) {}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 34a1fb8..65ea424 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -140,11 +140,6 @@
}
@Override
- public void markUseIdentifierNameString(DexEncodedMethod method) {
- method.getMutableOptimizationInfo().markUseIdentifierNameString();
- }
-
- @Override
public void markCheckNullReceiverBeforeAnySideEffect(DexEncodedMethod method, boolean mark) {
// Ignored.
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index def162e..dc67349 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -70,7 +70,7 @@
private static final int RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG = 0x10;
private static final int NEVER_RETURNS_NULL_FLAG = 0x20;
private static final int NEVER_RETURNS_NORMALLY_FLAG = 0x40;
- private static final int USE_IDENTIFIER_NAME_STRING_FLAG = 0x80;
+ private static final int UNUSED_FLAG = 0x80;
private static final int CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT_FLAG = 0x100;
private static final int TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT_FLAG = 0x200;
private static final int INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG = 0x400;
@@ -98,9 +98,7 @@
BooleanUtils.intValue(defaultOptInfo.neverReturnsNull()) * NEVER_RETURNS_NULL_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.neverReturnsNormally()) * NEVER_RETURNS_NORMALLY_FLAG;
- defaultFlags |=
- BooleanUtils.intValue(defaultOptInfo.useIdentifierNameString())
- * USE_IDENTIFIER_NAME_STRING_FLAG;
+ defaultFlags |= 0 * UNUSED_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.checksNullReceiverBeforeAnySideEffect())
* CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT_FLAG;
@@ -294,11 +292,6 @@
}
@Override
- public boolean useIdentifierNameString() {
- return isFlagSet(USE_IDENTIFIER_NAME_STRING_FLAG);
- }
-
- @Override
public boolean forceInline() {
return inlining == InlinePreference.ForceInline;
}
@@ -436,11 +429,6 @@
inlining = InlinePreference.NeverInline;
}
- // TODO(b/140214568): Should be package-private.
- public void markUseIdentifierNameString() {
- setFlag(USE_IDENTIFIER_NAME_STRING_FLAG);
- }
-
void markCheckNullReceiverBeforeAnySideEffect(boolean mark) {
setFlag(CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT_FLAG, mark);
}
@@ -492,7 +480,6 @@
// * returnsObjectWithLowerBoundType
// inlining: it is not inlined, and thus staticized. No more chance of inlining, though.
inlining = InlinePreference.Default;
- // useIdentifierNameString: code is not changed.
// checksNullReceiverBeforeAnySideEffect: no more receiver.
markCheckNullReceiverBeforeAnySideEffect(
DefaultMethodOptimizationInfo.UNKNOWN_CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT);
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 1d3718b..fd5df23 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -14,6 +14,7 @@
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -214,6 +215,10 @@
this.mappedNamingsByName = mappedNamingsByName;
}
+ public MappedRangesOfName getMappedRangesForRenamedName(String renamedName) {
+ return mappedRangesByRenamedName.get(renamedName);
+ }
+
@Override
public MemberNaming lookup(Signature renamedSignature) {
if (renamedSignature.kind() == SignatureKind.METHOD) {
@@ -276,6 +281,10 @@
}
}
+ public Collection<MemberNaming> allFieldNamings() {
+ return fieldMembers.values();
+ }
+
@Override
public <T extends Throwable> void forAllMethodNaming(
ThrowingConsumer<MemberNaming, T> consumer) throws T {
@@ -284,6 +293,10 @@
}
}
+ public Collection<MemberNaming> allMethodNamings() {
+ return methodMembers.values();
+ }
+
void write(Writer writer) throws IOException {
writer.append(originalName);
writer.append(" -> ");
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index 481a46c..72d5558 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -23,7 +23,6 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardClassFilter;
import com.android.tools.r8.utils.ThreadUtils;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -150,15 +149,6 @@
}
private void replaceDexItemBasedConstStringInMethod(DexEncodedMethod encodedMethod) {
- if (!encodedMethod.getOptimizationInfo().useIdentifierNameString()) {
- assert (encodedMethod.getCode().isDexCode()
- && Arrays.stream(encodedMethod.getCode().asDexCode().instructions)
- .noneMatch(Instruction::isDexItemBasedConstString))
- || (encodedMethod.getCode().isCfCode()
- && encodedMethod.getCode().asCfCode().instructions.stream()
- .noneMatch(CfInstruction::isDexItemBasedConstString));
- return;
- }
Code code = encodedMethod.getCode();
assert code != null;
if (code.isDexCode()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 89cf63c..a2a3380 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -139,6 +139,10 @@
public final Set<DexMethod> keepConstantArguments;
/** All methods that may not have any unused arguments removed. */
public final Set<DexMethod> keepUnusedArguments;
+ /** All methods that must be reprocessed (testing only). */
+ public final Set<DexMethod> reprocess;
+ /** All methods that must not be reprocessed (testing only). */
+ public final Set<DexMethod> neverReprocess;
/** All types that should be inlined if possible due to a configuration directive. */
public final PredicateSet<DexType> alwaysClassInline;
/** All types that *must* never be inlined due to a configuration directive (testing only). */
@@ -208,6 +212,8 @@
Set<DexMethod> whyAreYouNotInlining,
Set<DexMethod> keepConstantArguments,
Set<DexMethod> keepUnusedArguments,
+ Set<DexMethod> reprocess,
+ Set<DexMethod> neverReprocess,
PredicateSet<DexType> alwaysClassInline,
Set<DexType> neverClassInline,
Set<DexType> neverMerge,
@@ -246,6 +252,8 @@
this.whyAreYouNotInlining = whyAreYouNotInlining;
this.keepConstantArguments = keepConstantArguments;
this.keepUnusedArguments = keepUnusedArguments;
+ this.reprocess = reprocess;
+ this.neverReprocess = neverReprocess;
this.alwaysClassInline = alwaysClassInline;
this.neverClassInline = neverClassInline;
this.neverMerge = neverMerge;
@@ -287,6 +295,8 @@
Set<DexMethod> whyAreYouNotInlining,
Set<DexMethod> keepConstantArguments,
Set<DexMethod> keepUnusedArguments,
+ Set<DexMethod> reprocess,
+ Set<DexMethod> neverReprocess,
PredicateSet<DexType> alwaysClassInline,
Set<DexType> neverClassInline,
Set<DexType> neverMerge,
@@ -325,6 +335,8 @@
this.whyAreYouNotInlining = whyAreYouNotInlining;
this.keepConstantArguments = keepConstantArguments;
this.keepUnusedArguments = keepUnusedArguments;
+ this.reprocess = reprocess;
+ this.neverReprocess = neverReprocess;
this.alwaysClassInline = alwaysClassInline;
this.neverClassInline = neverClassInline;
this.neverMerge = neverMerge;
@@ -367,6 +379,8 @@
previous.whyAreYouNotInlining,
previous.keepConstantArguments,
previous.keepUnusedArguments,
+ previous.reprocess,
+ previous.neverReprocess,
previous.alwaysClassInline,
previous.neverClassInline,
previous.neverMerge,
@@ -416,6 +430,8 @@
previous.whyAreYouNotInlining,
previous.keepConstantArguments,
previous.keepUnusedArguments,
+ previous.reprocess,
+ previous.neverReprocess,
previous.alwaysClassInline,
previous.neverClassInline,
previous.neverMerge,
@@ -492,6 +508,8 @@
lense.rewriteMethodsWithRenamedSignature(previous.keepConstantArguments);
this.keepUnusedArguments =
lense.rewriteMethodsWithRenamedSignature(previous.keepUnusedArguments);
+ this.reprocess = lense.rewriteMethodsWithRenamedSignature(previous.reprocess);
+ this.neverReprocess = lense.rewriteMethodsWithRenamedSignature(previous.neverReprocess);
assert lense.assertDefinitionsNotModified(
previous.neverMerge.stream()
.map(this::definitionFor)
@@ -547,6 +565,8 @@
this.whyAreYouNotInlining = previous.whyAreYouNotInlining;
this.keepConstantArguments = previous.keepConstantArguments;
this.keepUnusedArguments = previous.keepUnusedArguments;
+ this.reprocess = previous.reprocess;
+ this.neverReprocess = previous.neverReprocess;
this.alwaysClassInline = previous.alwaysClassInline;
this.neverClassInline = previous.neverClassInline;
this.neverMerge = previous.neverMerge;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index cd9db29..f8cdb36 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -88,6 +88,7 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -2083,10 +2084,7 @@
return;
}
- // TODO(mkroghj): Remove pinnedItems check here.
- if (instantiatedTypes.contains(clazz)
- || instantiatedInterfaceTypes.contains(clazz)
- || pinnedItems.contains(clazz.type)) {
+ if (instantiatedTypes.contains(clazz) || instantiatedInterfaceTypes.contains(clazz)) {
markVirtualMethodAsLive(
clazz,
encodedPossibleTarget,
@@ -2250,7 +2248,23 @@
this.rootSet = rootSet;
this.dontWarnPatterns = dontWarnPatterns;
// Translate the result of root-set computation into enqueuer actions.
- enqueueRootItems(rootSet.noShrinking);
+ if (appView.options().isShrinking() || appView.options().getProguardConfiguration() == null) {
+ enqueueRootItems(rootSet.noShrinking);
+ } else {
+ // Add everything if we are not shrinking.
+ assert appView.options().getProguardConfiguration().getKeepAllRule() != null;
+ ImmutableSet<ProguardKeepRuleBase> keepAllSet =
+ ImmutableSet.of(appView.options().getProguardConfiguration().getKeepAllRule());
+ for (DexProgramClass dexProgramClass : appView.appInfo().classes()) {
+ for (DexEncodedMethod method : dexProgramClass.methods()) {
+ this.enqueueRootItem(method, keepAllSet);
+ }
+ for (DexEncodedField field : dexProgramClass.fields()) {
+ this.enqueueRootItem(field, keepAllSet);
+ }
+ this.enqueueRootItem(dexProgramClass, keepAllSet);
+ }
+ }
trace(executorService, timing);
options.reporter.failIfPendingErrors();
finalizeLibraryMethodOverrideInformation();
@@ -2332,6 +2346,8 @@
rootSet.whyAreYouNotInlining,
rootSet.keepConstantArguments,
rootSet.keepUnusedArguments,
+ rootSet.reprocess,
+ rootSet.neverReprocess,
rootSet.alwaysClassInline,
rootSet.neverClassInline,
rootSet.neverMerge,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 3c91bc8..8b86596 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -71,6 +71,7 @@
private boolean configurationDebugging = false;
private boolean dontUseMixedCaseClassnames = false;
private int maxRemovedAndroidLogLevel = 1;
+ private ProguardKeepRule keepAllRule;
private Builder(DexItemFactory dexItemFactory, Reporter reporter) {
this.dexItemFactory = dexItemFactory;
@@ -356,7 +357,8 @@
keepDirectories.build(),
configurationDebugging,
dontUseMixedCaseClassnames,
- maxRemovedAndroidLogLevel);
+ maxRemovedAndroidLogLevel,
+ keepAllRule);
reporter.failIfPendingErrors();
@@ -372,11 +374,15 @@
// shrinking or minification is turned off through the API, then add a match all rule
// which will apply that.
if (!isShrinking() || !isObfuscating() || !isOptimizing()) {
- addRule(ProguardKeepRule.defaultKeepAllRule(modifiers -> {
- modifiers.setAllowsShrinking(isShrinking());
- modifiers.setAllowsOptimization(isOptimizing());
- modifiers.setAllowsObfuscation(isObfuscating());
- }));
+ ProguardKeepRule rule =
+ ProguardKeepRule.defaultKeepAllRule(
+ modifiers -> {
+ modifiers.setAllowsShrinking(isShrinking());
+ modifiers.setAllowsOptimization(isOptimizing());
+ modifiers.setAllowsObfuscation(isObfuscating());
+ });
+ addRule(rule);
+ this.keepAllRule = rule;
}
if (keepRuleSynthesisForRecompilation) {
@@ -426,6 +432,7 @@
private final boolean configurationDebugging;
private final boolean dontUseMixedCaseClassnames;
private final int maxRemovedAndroidLogLevel;
+ private final ProguardKeepRule keepAllRule;
private ProguardConfiguration(
String parsedConfiguration,
@@ -466,7 +473,8 @@
ProguardPathFilter keepDirectories,
boolean configurationDebugging,
boolean dontUseMixedCaseClassnames,
- int maxRemovedAndroidLogLevel) {
+ int maxRemovedAndroidLogLevel,
+ ProguardKeepRule keepAllRule) {
this.parsedConfiguration = parsedConfiguration;
this.dexItemFactory = factory;
this.injars = ImmutableList.copyOf(injars);
@@ -506,6 +514,7 @@
this.configurationDebugging = configurationDebugging;
this.dontUseMixedCaseClassnames = dontUseMixedCaseClassnames;
this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel;
+ this.keepAllRule = keepAllRule;
}
/**
@@ -676,6 +685,10 @@
return maxRemovedAndroidLogLevel;
}
+ public ProguardKeepRule getKeepAllRule() {
+ return keepAllRule;
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index cc05e23..f1a2b0f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -472,6 +472,20 @@
configurationBuilder.addRule(rule);
return true;
}
+ if (acceptString("neverreprocessmethod")) {
+ configurationBuilder.addRule(
+ parseReprocessMethodRule(ReprocessMethodRule.Type.NEVER, optionStart));
+ return true;
+ }
+ if (acceptString("reprocessclassinitializer")) {
+ configurationBuilder.addRule(parseReprocessClassInitializerRule(optionStart));
+ return true;
+ }
+ if (acceptString("reprocessmethod")) {
+ configurationBuilder.addRule(
+ parseReprocessMethodRule(ReprocessMethodRule.Type.ALWAYS, optionStart));
+ return true;
+ }
if (acceptString("whyareyounotinlining")) {
WhyAreYouNotInliningRule rule = parseWhyAreYouNotInliningRule(optionStart);
configurationBuilder.addRule(rule);
@@ -807,6 +821,28 @@
return keepRuleBuilder.build();
}
+ private ReprocessClassInitializerRule parseReprocessClassInitializerRule(Position start)
+ throws ProguardRuleParserException {
+ ReprocessClassInitializerRule.Builder builder =
+ ReprocessClassInitializerRule.builder().setOrigin(origin).setStart(start);
+ parseClassSpec(builder, false);
+ Position end = getPosition();
+ builder.setSource(getSourceSnippet(contents, start, end));
+ builder.setEnd(end);
+ return builder.build();
+ }
+
+ private ReprocessMethodRule parseReprocessMethodRule(
+ ReprocessMethodRule.Type type, Position start) throws ProguardRuleParserException {
+ ReprocessMethodRule.Builder builder =
+ ReprocessMethodRule.builder().setOrigin(origin).setStart(start).setType(type);
+ parseClassSpec(builder, false);
+ Position end = getPosition();
+ builder.setSource(getSourceSnippet(contents, start, end));
+ builder.setEnd(end);
+ return builder.build();
+ }
+
private WhyAreYouNotInliningRule parseWhyAreYouNotInliningRule(Position start)
throws ProguardRuleParserException {
WhyAreYouNotInliningRule.Builder keepRuleBuilder =
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index f899a6b..41aa9c5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -65,6 +65,14 @@
return null;
}
+ public boolean isReprocessMethodRule() {
+ return false;
+ }
+
+ public ReprocessMethodRule asReprocessMethodRule() {
+ return null;
+ }
+
Iterable<DexProgramClass> relevantCandidatesForRule(
AppView<? extends AppInfoWithSubtyping> appView, Iterable<DexProgramClass> defaultValue) {
if (hasInheritanceClassName() && getInheritanceClassName().hasSpecificType()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java b/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java
new file mode 100644
index 0000000..fa7d352
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class ReprocessClassInitializerRule extends ProguardConfigurationRule {
+
+ public static class Builder
+ extends ProguardConfigurationRule.Builder<ReprocessClassInitializerRule, Builder> {
+
+ private Builder() {
+ super();
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ @Override
+ public ReprocessClassInitializerRule build() {
+ return new ReprocessClassInitializerRule(
+ origin,
+ getPosition(),
+ source,
+ classAnnotation,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotation,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+ }
+
+ private ReprocessClassInitializerRule(
+ Origin origin,
+ Position position,
+ String source,
+ ProguardTypeMatcher classAnnotation,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules) {
+ super(
+ origin,
+ position,
+ source,
+ classAnnotation,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotation,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return "reprocessclassinitializer";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ReprocessMethodRule.java b/src/main/java/com/android/tools/r8/shaking/ReprocessMethodRule.java
new file mode 100644
index 0000000..8e8e288
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ReprocessMethodRule.java
@@ -0,0 +1,120 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class ReprocessMethodRule extends ProguardConfigurationRule {
+
+ public enum Type {
+ ALWAYS,
+ NEVER
+ }
+
+ public static class Builder
+ extends ProguardConfigurationRule.Builder<ReprocessMethodRule, Builder> {
+
+ private Type type;
+
+ private Builder() {
+ super();
+ }
+
+ public Builder setType(Type type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ @Override
+ public ReprocessMethodRule build() {
+ return new ReprocessMethodRule(
+ origin,
+ getPosition(),
+ source,
+ classAnnotation,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotation,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules,
+ type);
+ }
+ }
+
+ private final Type type;
+
+ private ReprocessMethodRule(
+ Origin origin,
+ Position position,
+ String source,
+ ProguardTypeMatcher classAnnotation,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules,
+ Type type) {
+ super(
+ origin,
+ position,
+ source,
+ classAnnotation,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotation,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ this.type = type;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public boolean isReprocessMethodRule() {
+ return true;
+ }
+
+ @Override
+ public ReprocessMethodRule asReprocessMethodRule() {
+ return this;
+ }
+
+ @Override
+ String typeString() {
+ switch (type) {
+ case ALWAYS:
+ return "reprocessmethod";
+ case NEVER:
+ return "neverreprocessmethod";
+ default:
+ throw new Unreachable();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 1cfd120..4186091 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -83,6 +83,8 @@
private final Set<DexMethod> whyAreYouNotInlining = Sets.newIdentityHashSet();
private final Set<DexMethod> keepParametersWithConstantValue = Sets.newIdentityHashSet();
private final Set<DexMethod> keepUnusedArguments = Sets.newIdentityHashSet();
+ private final Set<DexMethod> reprocess = Sets.newIdentityHashSet();
+ private final Set<DexMethod> neverReprocess = Sets.newIdentityHashSet();
private final PredicateSet<DexType> alwaysClassInline = new PredicateSet<>();
private final Set<DexType> neverClassInline = Sets.newIdentityHashSet();
private final Set<DexType> neverMerge = Sets.newIdentityHashSet();
@@ -213,16 +215,15 @@
markMatchingOverriddenMethods(
appView.appInfo(), clazz, memberKeepRules, rule, null, true, ifRule);
markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
- } else if (rule instanceof ClassMergingRule) {
- if (allRulesSatisfied(memberKeepRules, clazz)) {
- markClass(clazz, rule, ifRule);
- }
} else if (rule instanceof InlineRule
|| rule instanceof ConstantArgumentRule
|| rule instanceof UnusedArgumentRule
+ || rule instanceof ReprocessMethodRule
|| rule instanceof WhyAreYouNotInliningRule) {
markMatchingMethods(clazz, memberKeepRules, rule, null, ifRule);
- } else if (rule instanceof ClassInlineRule) {
+ } else if (rule instanceof ClassInlineRule
+ || rule instanceof ClassMergingRule
+ || rule instanceof ReprocessClassInitializerRule) {
if (allRulesSatisfied(memberKeepRules, clazz)) {
markClass(clazz, rule, ifRule);
}
@@ -319,6 +320,8 @@
whyAreYouNotInlining,
keepParametersWithConstantValue,
keepUnusedArguments,
+ reprocess,
+ neverReprocess,
alwaysClassInline,
neverClassInline,
neverMerge,
@@ -1184,6 +1187,27 @@
keepParametersWithConstantValue.add(item.asDexEncodedMethod().method);
context.markAsUsed();
}
+ } else if (context instanceof ReprocessClassInitializerRule) {
+ DexProgramClass clazz = item.asProgramClass();
+ if (clazz != null && clazz.hasClassInitializer()) {
+ reprocess.add(clazz.getClassInitializer().method);
+ context.markAsUsed();
+ }
+ } else if (context.isReprocessMethodRule()) {
+ if (item.isDexEncodedMethod()) {
+ DexEncodedMethod method = item.asDexEncodedMethod();
+ switch (context.asReprocessMethodRule().getType()) {
+ case ALWAYS:
+ reprocess.add(method.method);
+ break;
+ case NEVER:
+ neverReprocess.add(method.method);
+ break;
+ default:
+ throw new Unreachable();
+ }
+ context.markAsUsed();
+ }
} else if (context instanceof UnusedArgumentRule) {
if (item.isDexEncodedMethod()) {
keepUnusedArguments.add(item.asDexEncodedMethod().method);
@@ -1208,6 +1232,8 @@
public final Set<DexMethod> whyAreYouNotInlining;
public final Set<DexMethod> keepConstantArguments;
public final Set<DexMethod> keepUnusedArguments;
+ public final Set<DexMethod> reprocess;
+ public final Set<DexMethod> neverReprocess;
public final PredicateSet<DexType> alwaysClassInline;
public final Set<DexType> neverClassInline;
public final Set<DexType> neverMerge;
@@ -1235,6 +1261,8 @@
Set<DexMethod> whyAreYouNotInlining,
Set<DexMethod> keepConstantArguments,
Set<DexMethod> keepUnusedArguments,
+ Set<DexMethod> reprocess,
+ Set<DexMethod> neverReprocess,
PredicateSet<DexType> alwaysClassInline,
Set<DexType> neverClassInline,
Set<DexType> neverMerge,
@@ -1259,6 +1287,8 @@
this.whyAreYouNotInlining = whyAreYouNotInlining;
this.keepConstantArguments = keepConstantArguments;
this.keepUnusedArguments = keepUnusedArguments;
+ this.reprocess = reprocess;
+ this.neverReprocess = neverReprocess;
this.alwaysClassInline = alwaysClassInline;
this.neverClassInline = neverClassInline;
this.neverMerge = Collections.unmodifiableSet(neverMerge);
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 d0a4fb1..5b0400f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1134,6 +1134,15 @@
return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.N);
}
+ public boolean isCallSiteOptimizationEnabled() {
+ return enablePropagationOfConstantsAtCallSites || enablePropagationOfDynamicTypesAtCallSites;
+ }
+
+ public void disableCallSiteOptimization() {
+ enablePropagationOfConstantsAtCallSites = false;
+ enablePropagationOfDynamicTypesAtCallSites = false;
+ }
+
public boolean isInterfaceMethodDesugaringEnabled() {
// This condition is to filter out tests that never set program consumer.
if (!hasConsumer()) {
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
new file mode 100644
index 0000000..64c44b5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.util.function.Predicate;
+
+public class IterableUtils {
+
+ public static <T> int firstIndexMatching(Iterable<T> iterable, Predicate<T> tester) {
+ int i = 0;
+ for (T element : iterable) {
+ if (tester.test(element)) {
+ return i;
+ }
+ i++;
+ }
+ return -1;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/NeverReprocessMethod.java b/src/test/java/com/android/tools/r8/NeverReprocessMethod.java
new file mode 100644
index 0000000..ffbfe5a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NeverReprocessMethod.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NeverReprocessMethod {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index d80f7cd..f847f34 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -36,6 +36,7 @@
enum AllowedDiagnosticMessages {
ALL,
+ ERROR,
INFO,
NONE,
WARNING
@@ -47,13 +48,16 @@
private AllowedDiagnosticMessages allowedDiagnosticMessages = AllowedDiagnosticMessages.NONE;
private boolean allowUnusedProguardConfigurationRules = false;
- private boolean enableInliningAnnotations = false;
- private boolean enableNeverClassInliningAnnotations = false;
- private boolean enableMergeAnnotations = false;
- private boolean enableMemberValuePropagationAnnotations = false;
private boolean enableConstantArgumentAnnotations = false;
- private boolean enableUnusedArgumentAnnotations = false;
+ private boolean enableInliningAnnotations = false;
+ private boolean enableMemberValuePropagationAnnotations = false;
+ private boolean enableMergeAnnotations = false;
+ private boolean enableNeverClassInliningAnnotations = false;
+ private boolean enableNeverReprocessMethodAnnotations = false;
+ private boolean enableReprocessClassInitializerAnnotations = false;
+ private boolean enableReprocessMethodAnnotations = false;
private boolean enableSideEffectAnnotations = false;
+ private boolean enableUnusedArgumentAnnotations = false;
private CollectingGraphConsumer graphConsumer = null;
private List<String> keepRules = new ArrayList<>();
private List<Path> mainDexRulesFiles = new ArrayList<>();
@@ -63,13 +67,16 @@
R8TestCompileResult internalCompile(
Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
throws CompilationFailedException {
- if (enableInliningAnnotations
- || enableNeverClassInliningAnnotations
- || enableMergeAnnotations
+ if (enableConstantArgumentAnnotations
+ || enableInliningAnnotations
|| enableMemberValuePropagationAnnotations
- || enableConstantArgumentAnnotations
- || enableUnusedArgumentAnnotations
- || enableSideEffectAnnotations) {
+ || enableMergeAnnotations
+ || enableNeverClassInliningAnnotations
+ || enableNeverReprocessMethodAnnotations
+ || enableReprocessClassInitializerAnnotations
+ || enableReprocessMethodAnnotations
+ || enableSideEffectAnnotations
+ || enableUnusedArgumentAnnotations) {
ToolHelper.allowTestProguardOptions(builder);
}
if (!keepRules.isEmpty()) {
@@ -131,6 +138,9 @@
case ALL:
compileResult.assertDiagnosticMessageThatMatches(new IsAnything<>());
break;
+ case ERROR:
+ compileResult.assertOnlyErrors();
+ break;
case INFO:
compileResult.assertOnlyInfos();
break;
@@ -247,6 +257,12 @@
return addOptionsModification(options -> options.testing.allowClassInlinerGracefulExit = true);
}
+ /**
+ * Allow info, warning, and error diagnostics.
+ *
+ * <p>This should only be used if a test has any of these diagnostic messages. Therefore, it is a
+ * failure if no such diagnostics are reported.
+ */
public T allowDiagnosticMessages() {
assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
allowedDiagnosticMessages = AllowedDiagnosticMessages.ALL;
@@ -257,6 +273,12 @@
return allowDiagnosticInfoMessages(true);
}
+ /**
+ * Allow info diagnostics if {@param condition} is true.
+ *
+ * <p>This should only be used if a test has at least one diagnostic info message. Therefore, it
+ * is a failure if no such diagnostics are reported.
+ */
public T allowDiagnosticInfoMessages(boolean condition) {
if (condition) {
assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
@@ -269,6 +291,12 @@
return allowDiagnosticWarningMessages(true);
}
+ /**
+ * Allow warning diagnostics if {@param condition} is true.
+ *
+ * <p>This should only be used if a test has at least one diagnostic warning message. Therefore,
+ * it is a failure if no such diagnostics are reported.
+ */
public T allowDiagnosticWarningMessages(boolean condition) {
if (condition) {
assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
@@ -277,6 +305,24 @@
return self();
}
+ public T allowDiagnosticErrorMessages() {
+ return allowDiagnosticErrorMessages(true);
+ }
+
+ /**
+ * Allow error diagnostics if {@param condition} is true.
+ *
+ * <p>This should only be used if a test has at least one diagnostic error message. Therefore, it
+ * is a failure if no such diagnostics are reported.
+ */
+ public T allowDiagnosticErrorMessages(boolean condition) {
+ if (condition) {
+ assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
+ allowedDiagnosticMessages = AllowedDiagnosticMessages.ERROR;
+ }
+ return self();
+ }
+
public T allowUnusedProguardConfigurationRules() {
return allowUnusedProguardConfigurationRules(true);
}
@@ -352,6 +398,35 @@
return self();
}
+ public T enableReprocessClassInitializerAnnotations() {
+ if (!enableReprocessClassInitializerAnnotations) {
+ enableReprocessClassInitializerAnnotations = true;
+ addInternalKeepRules(
+ "-reprocessclassinitializer @com.android.tools.r8.ReprocessClassInitializer class *");
+ }
+ return self();
+ }
+
+ public T enableReprocessMethodAnnotations() {
+ if (!enableReprocessMethodAnnotations) {
+ enableReprocessMethodAnnotations = true;
+ addInternalKeepRules(
+ "-reprocessmethod class * {", " @com.android.tools.r8.ReprocessMethod <methods>;", "}");
+ }
+ return self();
+ }
+
+ public T enableNeverReprocessMethodAnnotations() {
+ if (!enableNeverReprocessMethodAnnotations) {
+ enableNeverReprocessMethodAnnotations = true;
+ addInternalKeepRules(
+ "-neverreprocessmethod class * {",
+ " @com.android.tools.r8.NeverReprocessMethod <methods>;",
+ "}");
+ }
+ return self();
+ }
+
public T enableSideEffectAnnotations() {
if (!enableSideEffectAnnotations) {
enableSideEffectAnnotations = true;
diff --git a/src/test/java/com/android/tools/r8/ReprocessClassInitializer.java b/src/test/java/com/android/tools/r8/ReprocessClassInitializer.java
new file mode 100644
index 0000000..0b9c34a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ReprocessClassInitializer.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+public @interface ReprocessClassInitializer {}
diff --git a/src/test/java/com/android/tools/r8/ReprocessMethod.java b/src/test/java/com/android/tools/r8/ReprocessMethod.java
new file mode 100644
index 0000000..049d49b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ReprocessMethod.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface ReprocessMethod {}
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 6ccae1f..3d55195 100644
--- a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
@@ -10,43 +10,46 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.ProgramResourceProvider;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.StringConsumer.FileConsumer;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.VmTestRunner;
-import com.android.tools.r8.VmTestRunner.IgnoreForRangeOfVmVersions;
-import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.MoveException;
import com.android.tools.r8.debug.DebugTestBase;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
import com.android.tools.r8.debug.DexDebugTestConfig;
-import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.origin.Origin;
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.AndroidApp.Builder;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
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.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Streams;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -57,12 +60,15 @@
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.stream.IntStream;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
// TODO(christofferqa): Add tests to check that statically typed invocations on method handles
// continue to work after class merging.
-@RunWith(VmTestRunner.class)
+@RunWith(Parameterized.class)
public class VerticalClassMergerTest extends TestBase {
private static final Path CF_DIR =
@@ -78,6 +84,17 @@
private static final Path DONT_OPTIMIZE = Paths.get(ToolHelper.EXAMPLES_DIR)
.resolve("classmerging").resolve("keep-rules-dontoptimize.txt");
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public VerticalClassMergerTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
private void configure(InternalOptions options) {
options.enableSideEffectAnalysis = false;
options.enableUnusedInterfaceRemoval = false;
@@ -87,12 +104,13 @@
private void runR8(Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
throws IOException, ExecutionException, CompilationFailedException {
inspector =
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addProgramFiles(EXAMPLE_JAR)
.addKeepRuleFiles(proguardConfig)
.enableProguardTestOptions()
.noMinification()
.addOptionsModification(optionsConsumer)
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspector();
}
@@ -141,7 +159,7 @@
"classmerging.ArrayTypeCollisionTest$A",
"classmerging.ArrayTypeCollisionTest$B");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(
getProguardConfig(
EXAMPLE_KEEP,
@@ -203,7 +221,7 @@
// Run test.
runTestOnInput(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(
"-keep class " + main + " {",
" public static void main(...);",
@@ -237,7 +255,7 @@
ImmutableSet.of("classmerging.CallGraphCycleTest", "classmerging.CallGraphCycleTest$B");
for (int i = 0; i < 5; i++) {
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -261,19 +279,20 @@
"classmerging.ConflictInGeneratedNameTest$B");
CodeInspector inspector =
runTestOnInput(
- testForR8(Backend.DEX)
- .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .allowUnusedProguardConfigurationRules(),
- main,
- readProgramFiles(programFiles),
- preservedClassNames::contains,
- options -> {
- configure(options);
- // Avoid that direct methods in B get inlined.
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- },
- // Disable debug testing since the test has a method with "$classmerging$" in the name.
- null);
+ testForR8(parameters.getBackend())
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(
+ options ->
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ readProgramFiles(programFiles),
+ preservedClassNames::contains,
+ // Disable debug testing since the test has a method with "$classmerging$" in the
+ // name.
+ null)
+ .inspector();
ClassSubject clazzSubject = inspector.clazz("classmerging.ConflictInGeneratedNameTest$B");
assertThat(clazzSubject, isPresent());
@@ -335,7 +354,7 @@
"classmerging.FieldCollisionTest",
"classmerging.FieldCollisionTest$B");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -361,16 +380,14 @@
"classmerging.LambdaRewritingTest$FunctionImpl",
"classmerging.LambdaRewritingTest$InterfaceImpl");
runTestOnInput(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(options -> options.enableClassInlining = false)
.allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
- name -> preservedClassNames.contains(name) || name.contains("$Lambda$"),
- options -> {
- this.configure(options);
- options.enableClassInlining = false;
- });
+ name -> preservedClassNames.contains(name) || name.contains("$Lambda$"));
}
@Test
@@ -388,16 +405,14 @@
"classmerging.ConflictingInterfaceSignaturesTest",
"classmerging.ConflictingInterfaceSignaturesTest$InterfaceImpl");
runTestOnInput(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(options -> options.enableInlining = false)
.allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
- preservedClassNames::contains,
- options -> {
- this.configure(options);
- options.enableInlining = false;
- });
+ preservedClassNames::contains);
}
@Test
@@ -423,7 +438,7 @@
"classmerging.MethodCollisionTest$C",
"classmerging.MethodCollisionTest$D");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -447,7 +462,7 @@
"classmerging.NestedDefaultInterfaceMethodsTest$B",
"classmerging.NestedDefaultInterfaceMethodsTest$C");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -470,7 +485,7 @@
"classmerging.NestedDefaultInterfaceMethodsTest$B",
"classmerging.NestedDefaultInterfaceMethodsTest$C");
runTestOnInput(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -499,7 +514,7 @@
"classmerging.PinnedParameterTypesTest$InterfaceImpl",
"classmerging.PinnedParameterTypesTest$TestClass");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"))
.allowUnusedProguardConfigurationRules(),
main,
@@ -524,7 +539,7 @@
"classmerging.PinnedArrayParameterTypesTest$InterfaceImpl",
"classmerging.PinnedArrayParameterTypesTest$TestClass");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"))
.allowUnusedProguardConfigurationRules(),
main,
@@ -543,65 +558,60 @@
};
// Try without vertical class merging, to check that output is as expected.
- String expectedProguardMapWithoutClassMerging =
- Joiner.on(System.lineSeparator())
- .join(
- "classmerging.ProguardFieldMapTest -> classmerging.ProguardFieldMapTest:",
- " 1:2:void main(java.lang.String[]):10:11 -> main",
- "classmerging.ProguardFieldMapTest$A -> classmerging.ProguardFieldMapTest$A:",
- " 1:1:void <init>():14:14 -> <init>",
- " 2:2:void <init>():16:16 -> <init>",
- "classmerging.ProguardFieldMapTest$B -> classmerging.ProguardFieldMapTest$B:",
- " 1:1:void <init>():20:20 -> <init>",
- " 1:1:void test():23:23 -> test");
- runTestOnInput(
- testForR8(Backend.DEX)
- .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .allowUnusedProguardConfigurationRules(),
- main,
- readProgramFiles(programFiles),
- Predicates.alwaysTrue(),
- options -> {
- configure(options);
- options.enableVerticalClassMerging = false;
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- options.proguardMapConsumer =
- ToolHelper.consumeString(
- proguardMap ->
- assertThat(
- proguardMap, containsString(expectedProguardMapWithoutClassMerging)));
- });
+ R8TestCompileResult compileResultWithoutClassMerging =
+ runTestOnInput(
+ testForR8(parameters.getBackend())
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(
+ options -> {
+ options.enableVerticalClassMerging = false;
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ })
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ readProgramFiles(programFiles),
+ Predicates.alwaysTrue());
+
+ ClassNameMapper mappingWithoutClassMerging =
+ ClassNameMapper.mapperFromString(compileResultWithoutClassMerging.getProguardMap());
+ ClassNamingForNameMapper mappingsForClassAWithoutClassMerging =
+ mappingWithoutClassMerging.getClassNaming("classmerging.ProguardFieldMapTest$A");
+ assertTrue(mappingsForClassAWithoutClassMerging.allFieldNamings().isEmpty());
// Try with vertical class merging.
- String expectedProguardMapWithClassMerging =
- Joiner.on(System.lineSeparator())
- .join(
- "classmerging.ProguardFieldMapTest -> classmerging.ProguardFieldMapTest:",
- " 1:2:void main(java.lang.String[]):10:11 -> main",
- "classmerging.ProguardFieldMapTest$B -> classmerging.ProguardFieldMapTest$B:",
- " java.lang.String classmerging.ProguardFieldMapTest$A.f -> f",
- " 1:1:void classmerging.ProguardFieldMapTest$A.<init>():14:14 -> <init>",
- " 1:1:void <init>():20 -> <init>",
- " 2:2:void classmerging.ProguardFieldMapTest$A.<init>():16:16 -> <init>",
- " 2:2:void <init>():20 -> <init>",
- " 1:1:void test():23:23 -> test");
Set<String> preservedClassNames =
ImmutableSet.of("classmerging.ProguardFieldMapTest", "classmerging.ProguardFieldMapTest$B");
- runTestOnInput(
- testForR8(Backend.DEX)
- .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .allowUnusedProguardConfigurationRules(),
- main,
- readProgramFiles(programFiles),
- preservedClassNames::contains,
- options -> {
- configure(options);
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- options.proguardMapConsumer =
- ToolHelper.consumeString(
- proguardMap ->
- assertThat(proguardMap, containsString(expectedProguardMapWithClassMerging)));
- });
+ R8TestCompileResult compileResultWithClassMerging =
+ runTestOnInput(
+ testForR8(parameters.getBackend())
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(
+ options -> options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ readProgramFiles(programFiles),
+ preservedClassNames::contains);
+
+ ClassNameMapper mappingWithClassMerging =
+ ClassNameMapper.mapperFromString(compileResultWithClassMerging.getProguardMap());
+ assertNull(mappingWithClassMerging.getClassNaming("classmerging.ProguardFieldMapTest$A"));
+
+ ClassNamingForNameMapper mappingsForClassBWithClassMerging =
+ mappingWithClassMerging.getClassNaming("classmerging.ProguardFieldMapTest$B");
+ assertTrue(
+ mappingsForClassBWithClassMerging.allFieldNamings().stream()
+ .anyMatch(
+ fieldNaming ->
+ fieldNaming
+ .getOriginalSignature()
+ .toString()
+ .equals("java.lang.String classmerging.ProguardFieldMapTest$A.f")
+ && fieldNaming
+ .getRenamedSignature()
+ .toString()
+ .equals("java.lang.String f")));
}
@Test
@@ -615,68 +625,68 @@
};
// Try without vertical class merging, to check that output is as expected.
- String expectedProguardMapWithoutClassMerging =
- Joiner.on(System.lineSeparator())
- .join(
- "classmerging.ProguardMethodMapTest -> classmerging.ProguardMethodMapTest:",
- " 1:2:void main(java.lang.String[]):10:11 -> main",
- "classmerging.ProguardMethodMapTest$A -> classmerging.ProguardMethodMapTest$A:",
- " 1:1:void <init>():14:14 -> <init>",
- " 1:1:void method():17:17 -> method",
- "classmerging.ProguardMethodMapTest$B -> classmerging.ProguardMethodMapTest$B:",
- " 1:1:void <init>():22:22 -> <init>",
- " 1:2:void method():26:27 -> method");
- runTestOnInput(
- testForR8(Backend.DEX)
- .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .allowUnusedProguardConfigurationRules(),
- main,
- readProgramFiles(programFiles),
- Predicates.alwaysTrue(),
- options -> {
- configure(options);
- options.enableVerticalClassMerging = false;
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- options.proguardMapConsumer =
- ToolHelper.consumeString(
- proguardMap ->
- assertThat(
- proguardMap, containsString(expectedProguardMapWithoutClassMerging)));
- });
+ R8TestCompileResult compileResultWithoutClassMerging =
+ runTestOnInput(
+ testForR8(parameters.getBackend())
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(
+ options -> {
+ options.enableVerticalClassMerging = false;
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ })
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ readProgramFiles(programFiles),
+ Predicates.alwaysTrue());
+
+ ClassNameMapper mappingWithoutClassMerging =
+ ClassNameMapper.mapperFromString(compileResultWithoutClassMerging.getProguardMap());
+ ClassNamingForNameMapper mappingsForClassAWithoutClassMerging =
+ mappingWithoutClassMerging.getClassNaming("classmerging.ProguardMethodMapTest$A");
+ assertTrue(
+ mappingsForClassAWithoutClassMerging.allMethodNamings().stream()
+ .allMatch(
+ methodNaming ->
+ methodNaming
+ .getOriginalSignature()
+ .toString()
+ .equals(methodNaming.getRenamedSignature().toString())));
// Try with vertical class merging.
- String expectedProguardMapWithClassMerging =
- Joiner.on(System.lineSeparator())
- .join(
- "classmerging.ProguardMethodMapTest -> classmerging.ProguardMethodMapTest:",
- " 1:2:void main(java.lang.String[]):10:11 -> main",
- "classmerging.ProguardMethodMapTest$B -> classmerging.ProguardMethodMapTest$B:",
- // TODO(christofferqa): Should this be "...<init>():14 -> <init>" to reflect that
- // A.<init> has been inlined into B.<init>?
- " 1:1:void classmerging.ProguardMethodMapTest$A.<init>():14:14 -> <init>",
- // TODO(christofferqa): Should this be " ...<init>():21:21 -> <init>"?
- " 1:1:void <init>():22 -> <init>",
- " 1:2:void method():26:27 -> method",
- " 1:1:void classmerging.ProguardMethodMapTest$A.method():17:17 -> "
- + "method$classmerging$ProguardMethodMapTest$A");
Set<String> preservedClassNames =
ImmutableSet.of(
"classmerging.ProguardMethodMapTest", "classmerging.ProguardMethodMapTest$B");
- runTestOnInput(
- testForR8(Backend.DEX)
- .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .allowUnusedProguardConfigurationRules(),
- main,
- readProgramFiles(programFiles),
- preservedClassNames::contains,
- options -> {
- configure(options);
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- options.proguardMapConsumer =
- ToolHelper.consumeString(
- proguardMap ->
- assertThat(proguardMap, containsString(expectedProguardMapWithClassMerging)));
- });
+ R8TestCompileResult compileResultWithClassMerging =
+ runTestOnInput(
+ testForR8(parameters.getBackend())
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(
+ options -> options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ readProgramFiles(programFiles),
+ preservedClassNames::contains);
+
+ ClassNameMapper mappingWithClassMerging =
+ ClassNameMapper.mapperFromString(compileResultWithClassMerging.getProguardMap());
+ assertNull(mappingWithClassMerging.getClassNaming("classmerging.ProguardMethodMapTest$A"));
+
+ ClassNamingForNameMapper mappingsForClassBWithClassMerging =
+ mappingWithClassMerging.getClassNaming("classmerging.ProguardMethodMapTest$B");
+ assertTrue(
+ mappingsForClassBWithClassMerging.allMethodNamings().stream()
+ .anyMatch(
+ methodNaming ->
+ methodNaming
+ .getOriginalSignature()
+ .toString()
+ .equals("void classmerging.ProguardMethodMapTest$A.method()")
+ && methodNaming
+ .getRenamedSignature()
+ .toString()
+ .equals("void method$classmerging$ProguardMethodMapTest$A()")));
}
@Test
@@ -690,74 +700,80 @@
};
// Try without vertical class merging, to check that output is as expected.
- String expectedProguardMapWithoutClassMerging =
- Joiner.on(System.lineSeparator())
- .join(
- "classmerging.ProguardMethodMapTest -> classmerging.ProguardMethodMapTest:",
- " 1:2:void main(java.lang.String[]):10:11 -> main",
- "classmerging.ProguardMethodMapTest$A -> classmerging.ProguardMethodMapTest$A:",
- " 1:1:void <init>():14:14 -> <init>",
- "classmerging.ProguardMethodMapTest$B -> classmerging.ProguardMethodMapTest$B:",
- " 1:1:void <init>():22:22 -> <init>",
- " 1:1:void method():26:26 -> method",
- " 2:2:void classmerging.ProguardMethodMapTest$A.method():17:17 -> method",
- " 2:2:void method():27 -> method");
- runTestOnInput(
- testForR8(Backend.DEX)
- .addKeepRules(
- getProguardConfig(
- EXAMPLE_KEEP,
- "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
- + " method(); }"))
- .allowUnusedProguardConfigurationRules(),
- main,
- readProgramFiles(programFiles),
- Predicates.alwaysTrue(),
- options -> {
- configure(options);
- options.enableVerticalClassMerging = false;
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- options.proguardMapConsumer =
- ToolHelper.consumeString(
- proguardMap ->
- assertThat(
- proguardMap, containsString(expectedProguardMapWithoutClassMerging)));
- });
+ R8TestCompileResult compileResultWithoutClassMerging =
+ runTestOnInput(
+ testForR8(parameters.getBackend())
+ .addKeepRules(
+ getProguardConfig(
+ EXAMPLE_KEEP,
+ "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
+ + " method(); }"))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(
+ options -> {
+ options.enableVerticalClassMerging = false;
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ })
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ readProgramFiles(programFiles),
+ Predicates.alwaysTrue());
+
+ ClassNameMapper mappingWithoutClassMerging =
+ ClassNameMapper.mapperFromString(compileResultWithoutClassMerging.getProguardMap());
+ ClassNamingForNameMapper mappingsForClassBWithoutClassMerging =
+ mappingWithoutClassMerging.getClassNaming("classmerging.ProguardMethodMapTest$B");
+ assertTrue(
+ mappingsForClassBWithoutClassMerging
+ .getMappedRangesForRenamedName("method")
+ .getMappedRanges()
+ .stream()
+ .anyMatch(
+ mappedRange ->
+ mappedRange
+ .signature
+ .toString()
+ .equals("void classmerging.ProguardMethodMapTest$A.method()")
+ && mappedRange.renamedName.equals("method")));
// Try with vertical class merging.
- String expectedProguardMapWithClassMerging =
- Joiner.on(System.lineSeparator())
- .join(
- "classmerging.ProguardMethodMapTest -> classmerging.ProguardMethodMapTest:",
- " 1:2:void main(java.lang.String[]):10:11 -> main",
- "classmerging.ProguardMethodMapTest$B -> classmerging.ProguardMethodMapTest$B:",
- " 1:1:void classmerging.ProguardMethodMapTest$A.<init>():14:14 -> <init>",
- " 1:1:void <init>():22 -> <init>",
- " 1:1:void method():26:26 -> method",
- " 2:2:void classmerging.ProguardMethodMapTest$A.method():17:17 -> method",
- " 2:2:void method():27 -> method");
Set<String> preservedClassNames =
ImmutableSet.of(
"classmerging.ProguardMethodMapTest", "classmerging.ProguardMethodMapTest$B");
- runTestOnInput(
- testForR8(Backend.DEX)
- .addKeepRules(
- getProguardConfig(
- EXAMPLE_KEEP,
- "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
- + " method(); }"))
- .allowUnusedProguardConfigurationRules(),
- main,
- readProgramFiles(programFiles),
- preservedClassNames::contains,
- options -> {
- configure(options);
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- options.proguardMapConsumer =
- ToolHelper.consumeString(
- proguardMap ->
- assertThat(proguardMap, containsString(expectedProguardMapWithClassMerging)));
- });
+ R8TestCompileResult compileResultWithClassMerging =
+ runTestOnInput(
+ testForR8(parameters.getBackend())
+ .addKeepRules(
+ getProguardConfig(
+ EXAMPLE_KEEP,
+ "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
+ + " method(); }"))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(
+ options -> {
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ })
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ readProgramFiles(programFiles),
+ preservedClassNames::contains);
+
+ ClassNameMapper mappingWithClassMerging =
+ ClassNameMapper.mapperFromString(compileResultWithClassMerging.getProguardMap());
+ ClassNamingForNameMapper mappingsForClassBWithClassMerging =
+ mappingWithClassMerging.getClassNaming("classmerging.ProguardMethodMapTest$B");
+ assertTrue(
+ mappingsForClassBWithClassMerging
+ .getMappedRangesForRenamedName("method")
+ .getMappedRanges()
+ .stream()
+ .anyMatch(
+ mappedRange ->
+ mappedRange
+ .signature
+ .toString()
+ .equals("void classmerging.ProguardMethodMapTest$A.method()")
+ && mappedRange.renamedName.equals("method")));
}
@Test
@@ -774,7 +790,7 @@
"classmerging.SubClassThatReferencesSuperMethod",
"classmerging.SuperCallRewritingTest");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -792,6 +808,8 @@
// merging, R8 should not rewrite the invoke-super instruction into invoke-direct.
@Test
public void testSuperCallNotRewrittenToDirect() throws Throwable {
+ assumeTrue(parameters.isDexRuntime()); // Due to smali input.
+
String main = "classmerging.SuperCallRewritingTest";
Path[] programFiles =
new Path[] {
@@ -834,7 +852,7 @@
.run(main)
.assertSuccessWithOutput(expectedOutput);
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addOptionsModification(this::configure)
.addKeepMainRule(main)
// Keep the classes to avoid merge, but don't keep methods which allows inlining.
@@ -876,8 +894,11 @@
// public void invokeMethodOnF() { "invoke-super F.m()" }
// }
@Test
- @IgnoreForRangeOfVmVersions(from = Version.V5_1_1, to = Version.V6_0_1)
public void testSuperCallToMergedClassIsRewritten() throws Throwable {
+ assumeTrue(parameters.isDexRuntime()); // Due to smali input.
+ assumeFalse(parameters.getRuntime().asDex().getVm().getVersion() == Version.V5_1_1);
+ assumeFalse(parameters.getRuntime().asDex().getVm().getVersion() == Version.V6_0_1);
+
String main = "classmerging.SuperCallToMergedClassIsRewrittenTest";
Set<String> preservedClassNames =
ImmutableSet.of(
@@ -986,7 +1007,7 @@
// Run test.
runTestOnInput(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(String.format("-keep class %s { public static void main(...); }", main)),
main,
appBuilder.build(),
@@ -1016,18 +1037,19 @@
"classmerging.SyntheticBridgeSignaturesTest$ASub",
"classmerging.SyntheticBridgeSignaturesTest$BSub");
runTestOnInput(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .addOptionsModification(this::configure)
+ .addOptionsModification(
+ options -> {
+ if (!allowInlining) {
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ }
+ })
.allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
preservedClassNames::contains,
- options -> {
- this.configure(options);
- if (!allowInlining) {
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- }
- },
// TODO(christofferqa): The debug test fails when inlining is not allowed.
allowInlining ? new VerticalClassMergerDebugTest(main) : null);
}
@@ -1055,7 +1077,7 @@
"classmerging.ConflictingInterfaceSignaturesTest",
"classmerging.ConflictingInterfaceSignaturesTest$InterfaceImpl");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -1083,12 +1105,13 @@
"classmerging.ExceptionTest$Exception2");
CodeInspector inspector =
runTest(
- testForR8(Backend.DEX)
- .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .allowUnusedProguardConfigurationRules(),
- main,
- programFiles,
- preservedClassNames::contains);
+ testForR8(parameters.getBackend())
+ .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+ .allowUnusedProguardConfigurationRules(),
+ main,
+ programFiles,
+ preservedClassNames::contains)
+ .inspector();
ClassSubject mainClass = inspector.clazz(main);
assertThat(mainClass, isPresent());
@@ -1098,14 +1121,11 @@
assertThat(mainMethod, isPresent());
// Check that the second catch handler has been removed.
- DexCode code = mainMethod.getMethod().getCode().asDexCode();
- int numberOfMoveExceptionInstructions = 0;
- for (Instruction instruction : code.instructions) {
- if (instruction instanceof MoveException) {
- numberOfMoveExceptionInstructions++;
- }
- }
- assertEquals(2, numberOfMoveExceptionInstructions);
+ assertEquals(
+ 2,
+ Streams.stream(mainMethod.iterateTryCatches())
+ .flatMapToInt(x -> IntStream.of(x.getNumberOfHandlers()))
+ .sum());
}
@Test
@@ -1140,7 +1160,7 @@
containsString("invokeinterface classmerging.MergeDefaultMethodIntoClassTest$A.f()V"));
runTestOnInput(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -1164,7 +1184,7 @@
"classmerging.ClassWithNativeMethodTest$A",
"classmerging.ClassWithNativeMethodTest$B");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -1195,7 +1215,7 @@
"classmerging.pkg.SimpleInterfaceImplRetriever",
"classmerging.pkg.SimpleInterfaceImplRetriever$SimpleInterfaceImpl");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -1226,7 +1246,7 @@
// Allow access modifications (and prevent SimpleInterfaceImplRetriever from being removed as
// a result of inlining).
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(
getProguardConfig(
EXAMPLE_KEEP,
@@ -1257,7 +1277,7 @@
"classmerging.RewritePinnedMethodTest$A",
"classmerging.RewritePinnedMethodTest$C");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(
getProguardConfig(
EXAMPLE_KEEP, "-keep class classmerging.RewritePinnedMethodTest$A { *; }"))
@@ -1280,7 +1300,7 @@
ImmutableSet.of(
"classmerging.TemplateMethodTest", "classmerging.TemplateMethodTest$AbstractClassImpl");
runTest(
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules(),
main,
@@ -1288,7 +1308,7 @@
preservedClassNames::contains);
}
- private CodeInspector runTest(
+ private R8TestCompileResult runTest(
R8FullTestBuilder builder,
String main,
Path[] programFiles,
@@ -1297,40 +1317,27 @@
return runTestOnInput(builder, main, readProgramFiles(programFiles), preservedClassNames);
}
- private CodeInspector runTestOnInput(
+ private R8TestCompileResult runTestOnInput(
R8FullTestBuilder builder,
String main,
AndroidApp input,
Predicate<String> preservedClassNames)
throws Throwable {
- return runTestOnInput(builder, main, input, preservedClassNames, this::configure);
- }
-
- private CodeInspector runTestOnInput(
- R8FullTestBuilder builder,
- String main,
- AndroidApp input,
- Predicate<String> preservedClassNames,
- Consumer<InternalOptions> optionsConsumer)
- throws Throwable {
return runTestOnInput(
- builder,
+ builder.addOptionsModification(this::configure),
main,
input,
preservedClassNames,
- optionsConsumer,
new VerticalClassMergerDebugTest(main));
}
- private CodeInspector runTestOnInput(
+ private R8TestCompileResult runTestOnInput(
R8FullTestBuilder builder,
String main,
AndroidApp input,
Predicate<String> preservedClassNames,
- Consumer<InternalOptions> optionsConsumer,
VerticalClassMergerDebugTest debugTestRunner)
throws Throwable {
- Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
R8TestCompileResult compileResult =
builder
.apply(
@@ -1344,13 +1351,12 @@
})
.noMinification()
.enableProguardTestOptions()
- .addOptionsModification(
- o -> {
- optionsConsumer.accept(o);
- o.proguardMapConsumer = new FileConsumer(proguardMapPath, o.proguardMapConsumer);
- })
+ .setMinApi(parameters.getApiLevel())
.compile();
+ Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
+ FileUtils.writeTextFile(proguardMapPath, compileResult.getProguardMap());
+
CodeInspector inputInspector = new CodeInspector(input);
CodeInspector outputInspector = compileResult.inspector();
@@ -1371,13 +1377,14 @@
.run(main)
.assertSuccess()
.getStdOut();
- compileResult.run(main).assertSuccessWithOutput(d8Result);
+
+ compileResult.run(parameters.getRuntime(), main).assertSuccessWithOutput(d8Result);
// Check that we never come across a method that has a name with "$classmerging$" in it during
// debugging.
- if (debugTestRunner != null) {
+ if (debugTestRunner != null && parameters.isDexRuntime()) {
debugTestRunner.test(compileResult.app, proguardMapPath);
}
- return outputInspector;
+ return compileResult;
}
private String getProguardConfig(Path path, String... additionalRules) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
index 75982e9..867b61c 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
@@ -68,7 +68,7 @@
testIncompleteNestError();
}
- private TestCompileResult compileOnlyClassesMatching(
+ private TestCompileResult<?, ?> compileOnlyClassesMatching(
Matcher<String> matcher,
boolean d8,
boolean allowDiagnosticWarningMessages,
@@ -142,7 +142,8 @@
private void testIncompleteNestWarning(boolean d8, boolean desugarWarning) throws Exception {
Matcher<String> innerClassMatcher = endsWith("BasicNestHostWithInnerClassMethods");
- TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true);
+ TestCompileResult<?, ?> compileResult =
+ compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true);
assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
if (desugarWarning) {
assertTrue(
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java
new file mode 100644
index 0000000..d2d8c86
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugaring.interfacemethods;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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 Regress148461139 extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public Regress148461139(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ // This only failed if tree-shaking was disabled.
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Condition.class)
+ .noTreeShaking()
+ .compile()
+ .inspectProguardConfiguration(p -> {
+ assertNotNull(p.getKeepAllRule());
+ })
+ .inspect(i -> assertTrue(i.clazz(Condition.class).isAbstract()));
+ }
+
+ public interface Condition {
+ boolean isTrue();
+
+ static Condition runOnUiThread() {
+ return () -> true;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalStaticGetCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalStaticGetCanonicalizationTest.java
new file mode 100644
index 0000000..fbec6ad
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalStaticGetCanonicalizationTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.canonicalization;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.ReprocessClassInitializer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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 IllegalStaticGetCanonicalizationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public IllegalStaticGetCanonicalizationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(IllegalStaticGetCanonicalizationTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(options -> options.enableRedundantFieldLoadElimination = false)
+ .enableInliningAnnotations()
+ .enableReprocessClassInitializerAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello!", "Hello!");
+ }
+
+ @ReprocessClassInitializer
+ static class TestClass {
+
+ static TestClass INSTANCE;
+
+ static {
+ INSTANCE = new TestClass();
+ INSTANCE.method();
+ INSTANCE.method();
+ }
+
+ public static void main(String[] args) {}
+
+ @NeverInline
+ void method() {
+ System.out.println("Hello!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java
new file mode 100644
index 0000000..4b8ceff
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java
@@ -0,0 +1,131 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverReprocessMethod;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.Collection;
+import java.util.function.Function;
+import java.util.function.Predicate;
+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 FieldWriteBeforeFieldReadTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public FieldWriteBeforeFieldReadTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(FieldWriteBeforeFieldReadTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> {
+ options.testing.waveModifier =
+ (waves) -> {
+ Function<String, Predicate<Collection<DexEncodedMethod>>> wavePredicate =
+ methodName ->
+ wave ->
+ wave.stream()
+ .anyMatch(
+ method ->
+ method.method.toSourceString().contains(methodName));
+ int readFieldsWaveIndex =
+ IterableUtils.firstIndexMatching(waves, wavePredicate.apply("readFields"));
+ assertTrue(readFieldsWaveIndex >= 0);
+ int writeFieldsWaveIndex =
+ IterableUtils.firstIndexMatching(waves, wavePredicate.apply("writeFields"));
+ // TODO(b/144739580): Should be false.
+ assertTrue(writeFieldsWaveIndex >= readFieldsWaveIndex);
+ };
+ })
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNeverReprocessMethodAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Live!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+ assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent());
+ // TODO(b/144739580): Should be absent.
+ assertThat(testClassSubject.uniqueMethodWithName("dead"), isPresent());
+ }
+
+ static class TestClass {
+
+ static boolean alwaysFalse;
+
+ public static void main(String[] args) {
+ A obj = new A();
+ writeFields(obj);
+ readFields(obj);
+ }
+
+ @NeverInline
+ static void writeFields(A obj) {
+ alwaysFalse = false;
+ obj.alwaysFalse = false;
+ increaseDistanceToNearestLeaf();
+ }
+
+ static void increaseDistanceToNearestLeaf() {}
+
+ @NeverInline
+ @NeverReprocessMethod
+ static void readFields(A obj) {
+ if (alwaysFalse || obj.alwaysFalse) {
+ dead();
+ } else {
+ live();
+ }
+ }
+
+ @NeverInline
+ static void live() {
+ System.out.println("Live!");
+ }
+
+ @NeverInline
+ static void dead() {
+ System.out.println("Dead!");
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ boolean alwaysFalse;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
new file mode 100644
index 0000000..83831d1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+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.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+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 AbstractInMiddleTest extends TestBase {
+
+ public static final String[] EXPECTED = new String[] {"A.foo", "C.foo", "C.foo", "C.foo"};
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public AbstractInMiddleTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.useRuntimeAsNoneRuntime());
+ AppInfoWithLiveness appInfo =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, B.class, C.class, Main.class).build(), Main.class)
+ .appInfo();
+ DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ Set<String> targets =
+ appInfo.resolveMethod(method.holder, method).lookupVirtualTargets(appInfo).stream()
+ .map(DexEncodedMethod::qualifiedName)
+ .collect(Collectors.toSet());
+ // TODO(b/148591377): Should we report B.foo()?
+ ImmutableSet<String> expected =
+ ImmutableSet.of(
+ A.class.getTypeName() + ".foo",
+ B.class.getTypeName() + ".foo",
+ C.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ }
+
+ @Test
+ public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addInnerClasses(AbstractInMiddleTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(AbstractInMiddleTest.class)
+ .enableMergeAnnotations()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @NeverMerge
+ @NeverClassInline
+ public static class A {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("A.foo");
+ }
+ }
+
+ @NeverMerge
+ public abstract static class B extends A {
+
+ @Override
+ public abstract void foo();
+ }
+
+ @NeverMerge
+ @NeverClassInline
+ public static class C extends B {
+
+ @Override
+ @NeverInline
+ public void foo() {
+ System.out.println("C.foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A().foo();
+ new C().foo();
+ ((A) new C()).foo();
+ ((B) new C()).foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
new file mode 100644
index 0000000..988f1a3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+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.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+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 DefaultInterfaceMethodInSubInterfaceSubTypeTest extends TestBase {
+
+ private static final String EXPECTED = "J.foo";
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public DefaultInterfaceMethodInSubInterfaceSubTypeTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.useRuntimeAsNoneRuntime());
+ AppInfoWithLiveness appInfo =
+ computeAppViewWithLiveness(
+ buildClasses(I.class, J.class, A.class, B.class, Main.class).build(), Main.class)
+ .appInfo();
+ DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ Set<String> targets =
+ appInfo.resolveMethod(method.holder, method).lookupVirtualTargets(appInfo).stream()
+ .map(DexEncodedMethod::qualifiedName)
+ .collect(Collectors.toSet());
+ // TODO(b/148591377): I.foo() should ideally not be included in the set.
+ ImmutableSet<String> expected =
+ ImmutableSet.of(I.class.getTypeName() + ".foo", J.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ }
+
+ @Test
+ public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addInnerClasses(DefaultInterfaceMethodInSubInterfaceSubTypeTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(DefaultInterfaceMethodInSubInterfaceSubTypeTest.class)
+ .addKeepMainRule(Main.class)
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @NeverMerge
+ public interface I {
+ void foo();
+ }
+
+ @NeverMerge
+ public interface J extends I {
+ @Override
+ @NeverInline
+ default void foo() {
+ System.out.println("J.foo");
+ }
+ }
+
+ @NeverMerge
+ public abstract static class A implements I {}
+
+ @NeverClassInline
+ public static class B extends A implements J {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ ((A) new B()).foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
new file mode 100644
index 0000000..89f17da
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+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.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+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 DefaultInterfaceMethodInSubInterfaceTest extends TestBase {
+
+ private static final String EXPECTED = "J.foo";
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public DefaultInterfaceMethodInSubInterfaceTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.useRuntimeAsNoneRuntime());
+ AppInfoWithLiveness appInfo =
+ computeAppViewWithLiveness(
+ buildClasses(I.class, J.class, A.class, Main.class).build(), Main.class)
+ .appInfo();
+ DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ Set<String> targets =
+ appInfo.resolveMethod(method.holder, method).lookupVirtualTargets(appInfo).stream()
+ .map(DexEncodedMethod::qualifiedName)
+ .collect(Collectors.toSet());
+ ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ }
+
+ @Test
+ public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addInnerClasses(DefaultInterfaceMethodInSubInterfaceTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(DefaultInterfaceMethodInSubInterfaceTest.class)
+ .addKeepMainRule(Main.class)
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @NeverMerge
+ public interface I {
+ void foo();
+ }
+
+ @NeverMerge
+ public interface J extends I {
+ @Override
+ @NeverInline
+ default void foo() {
+ System.out.println("J.foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class A implements J {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
new file mode 100644
index 0000000..2f979d9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+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.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+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 InvokeVirtualToInterfaceDefinitionTest extends TestBase {
+
+ private static final String EXPECTED = "I.foo";
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InvokeVirtualToInterfaceDefinitionTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.useRuntimeAsNoneRuntime());
+ AppInfoWithLiveness appInfo =
+ computeAppViewWithLiveness(buildClasses(A.class, I.class, Main.class).build(), Main.class)
+ .appInfo();
+ DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ Set<String> targets =
+ appInfo.resolveMethod(method.holder, method).lookupVirtualTargets(appInfo).stream()
+ .map(DexEncodedMethod::qualifiedName)
+ .collect(Collectors.toSet());
+ ImmutableSet<String> expected = ImmutableSet.of(I.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ }
+
+ @Test
+ public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
+ testForRuntime(parameters)
+ .addInnerClasses(InvokeVirtualToInterfaceDefinitionTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InvokeVirtualToInterfaceDefinitionTest.class)
+ .addKeepMainRule(Main.class)
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @NeverMerge
+ public interface I {
+
+ @NeverInline
+ default void foo() {
+ System.out.println("I.foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class A implements I {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
new file mode 100644
index 0000000..45c208a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.resolution.virtualtargets.package_a.Middle;
+import com.android.tools.r8.resolution.virtualtargets.package_a.Top;
+import com.android.tools.r8.resolution.virtualtargets.package_a.TopRunner;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PackagePrivateChainTest extends TestBase {
+
+ private static final String[] EXPECTED = new String[] {"Middle.clear()", "Bottom.clear()"};
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public PackagePrivateChainTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
+ TestRunResult<?> runResult =
+ testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+ .addProgramClasses(Top.class, Middle.class, Bottom.class, TopRunner.class, Main.class)
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
+ runResult.assertFailureWithErrorThatMatches(containsString("clear overrides final"));
+ } else {
+ runResult.assertSuccessWithOutputLines(EXPECTED);
+ }
+ }
+
+ @Test
+ public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+ // TODO(b/148584615): Fix test.
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Top.class, Middle.class, Bottom.class, TopRunner.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Bottom.clear()", "Bottom.clear()");
+ }
+
+ public static class Bottom extends Middle {
+
+ public void clear() {
+ System.out.println("Bottom.clear()");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Bottom bottom = new Bottom();
+ TopRunner.run(bottom);
+ bottom.clear();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
new file mode 100644
index 0000000..e786da6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
@@ -0,0 +1,200 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.resolution.virtualtargets.package_a.ViewModel;
+import com.android.tools.r8.resolution.virtualtargets.package_a.ViewModelRunner;
+import com.android.tools.r8.resolution.virtualtargets.package_a.ViewModelRunnerWithCast;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PackagePrivateFinalOverrideTest extends TestBase {
+
+ private static final String[] EXPECTED =
+ new String[] {"ViewModel.clear()", "MyViewModel.clear()", "ViewModel.clear()"};
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public PackagePrivateFinalOverrideTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
+ TestRunResult<?> runResult =
+ testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+ .addProgramClasses(
+ MyViewModel.class, ViewModel.class, Main.class, ViewModelRunner.class)
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
+ runResult.assertFailureWithErrorThatMatches(containsString("clear overrides final"));
+ } else {
+ runResult.assertSuccessWithOutputLines(EXPECTED);
+ }
+ }
+
+ @Test
+ public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(
+ MyViewModel.class, Main.class, ViewModel.class, ViewModelRunner.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
+ runResult.assertFailureWithErrorThatMatches(containsString("overrides final"));
+ } else {
+ runResult.assertFailureWithErrorThatMatches(containsString("java.lang.NullPointerException"));
+ }
+ }
+
+ @Test
+ public void testRuntimeWithInvalidInvoke()
+ throws ExecutionException, CompilationFailedException, IOException {
+ TestRunResult<?> runResult =
+ testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+ .addProgramClasses(MyViewModel.class, ViewModel.class, ViewModelRunner.class)
+ .addProgramClassFileData(getModifiedMainWithIllegalInvokeToViewModelClear())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
+ runResult.assertFailureWithErrorThatMatches(containsString("clear overrides final"));
+ } else {
+ runResult.assertFailureWithErrorThatMatches(containsString("java.lang.IllegalAccessError"));
+ }
+ }
+
+ @Test
+ public void testR8WithInvalidInvoke()
+ throws ExecutionException, CompilationFailedException, IOException {
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(MyViewModel.class, ViewModel.class, ViewModelRunner.class)
+ .addProgramClassFileData(getModifiedMainWithIllegalInvokeToViewModelClear())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
+ runResult.assertFailureWithErrorThatMatches(containsString("overrides final"));
+ } else {
+ runResult.assertFailureWithErrorThatMatches(containsString("java.lang.NullPointerException"));
+ }
+ }
+
+ @Test
+ public void testRuntimeWithAmbiguousInvoke()
+ throws ExecutionException, CompilationFailedException, IOException {
+ TestRunResult<?> runResult =
+ testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+ .addProgramClasses(MyViewModel.class, ViewModel.class, Main.class)
+ .addProgramClassFileData(getModifiedViewModelRunnerWithDirectMyViewModelTarget())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
+ runResult.assertFailureWithErrorThatMatches(containsString("clear overrides final"));
+ } else {
+ runResult.assertSuccessWithOutputLines(
+ "ViewModel.clear()", "MyViewModel.clear()", "MyViewModel.clear()");
+ }
+ }
+
+ @Test
+ public void testR8WithAmbiguousInvoke()
+ throws ExecutionException, CompilationFailedException, IOException {
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(MyViewModel.class, ViewModel.class, Main.class)
+ .addProgramClassFileData(getModifiedViewModelRunnerWithDirectMyViewModelTarget())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
+ runResult.assertFailureWithErrorThatMatches(containsString("overrides final"));
+ } else {
+ runResult.assertFailureWithErrorThatMatches(containsString("java.lang.NullPointerException"));
+ }
+ }
+
+ private byte[] getModifiedMainWithIllegalInvokeToViewModelClear() throws IOException {
+ return transformer(Main.class)
+ .transformMethodInsnInMethod(
+ "main",
+ (opcode, owner, name, descriptor, isInterface, continuation) -> {
+ if (name.equals("clear")) {
+ continuation.apply(
+ opcode,
+ DescriptorUtils.getBinaryNameFromJavaType(ViewModel.class.getTypeName()),
+ name,
+ descriptor,
+ isInterface);
+ } else {
+ continuation.apply(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform();
+ }
+
+ private byte[] getModifiedViewModelRunnerWithDirectMyViewModelTarget() throws IOException {
+ return transformer(ViewModelRunnerWithCast.class)
+ .setClassDescriptor(
+ DescriptorUtils.javaTypeToDescriptor(ViewModelRunner.class.getTypeName()))
+ .transformMethodInsnInMethod(
+ "run",
+ (opcode, owner, name, descriptor, isInterface, continuation) -> {
+ if (name.equals("clearBridge")) {
+ continuation.apply(opcode, owner, "clear", descriptor, isInterface);
+ } else {
+ continuation.apply(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform();
+ }
+
+ @NeverClassInline
+ public static class MyViewModel extends ViewModel {
+
+ @NeverInline
+ public void clear() {
+ System.out.println("MyViewModel.clear()");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ MyViewModel myViewModel = new MyViewModel();
+ myViewModel.clearBridge();
+ myViewModel.clear();
+ ViewModelRunner.run(myViewModel);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
new file mode 100644
index 0000000..5bef2ca
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+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.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+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 TargetInDefaultMethodTest extends TestBase {
+
+ private static final String EXPECTED = "I.foo";
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public TargetInDefaultMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.useRuntimeAsNoneRuntime());
+ AppInfoWithLiveness appInfo =
+ computeAppViewWithLiveness(
+ buildClasses(I.class, A.class, B.class, C.class, Main.class).build(), Main.class)
+ .appInfo();
+ DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
+ ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+ Set<String> targets =
+ resolutionResult.lookupInterfaceTargets(appInfo.withSubtyping()).stream()
+ .map(DexEncodedMethod::qualifiedName)
+ .collect(Collectors.toSet());
+ ImmutableSet<String> expected =
+ ImmutableSet.of(B.class.getTypeName() + ".foo", I.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ }
+
+ @Test
+ public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addInnerClasses(TargetInDefaultMethodTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(TargetInDefaultMethodTest.class)
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @NeverMerge
+ public interface I {
+ @NeverInline
+ default void foo() {
+ System.out.println("I.foo");
+ }
+ }
+
+ @NeverMerge
+ public abstract static class A implements I {}
+
+ @NeverClassInline
+ public static class B extends A {
+
+ @Override
+ @NeverInline
+ public void foo() {
+ System.out.println("B.foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class C extends A {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ callA(args.length == 0 ? new C() : new B());
+ }
+
+ @NeverInline
+ private static void callA(A a) {
+ a.foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/Middle.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/Middle.java
new file mode 100644
index 0000000..44b019d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/Middle.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets.package_a;
+
+public class Middle extends Top {
+
+ @Override
+ final void clear() {
+ System.out.println("Middle.clear()");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/Top.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/Top.java
new file mode 100644
index 0000000..5d20def
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/Top.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets.package_a;
+
+public class Top {
+
+ void clear() {
+ System.out.println("Top.clear()");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/TopRunner.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/TopRunner.java
new file mode 100644
index 0000000..1f96c07
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/TopRunner.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets.package_a;
+
+public class TopRunner {
+
+ public static void run(Top top) {
+ top.clear();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/ViewModel.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/ViewModel.java
new file mode 100644
index 0000000..2c5f612
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/ViewModel.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets.package_a;
+
+public abstract class ViewModel {
+
+ final void clear() {
+ System.out.println("ViewModel.clear()");
+ }
+
+ public void clearBridge() {
+ clear();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/ViewModelRunner.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/ViewModelRunner.java
new file mode 100644
index 0000000..87cc5ac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/ViewModelRunner.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets.package_a;
+
+public class ViewModelRunner {
+
+ public static void run(ViewModel vm) {
+ vm.clear();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/ViewModelRunnerWithCast.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/ViewModelRunnerWithCast.java
new file mode 100644
index 0000000..9cceffa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/package_a/ViewModelRunnerWithCast.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.virtualtargets.package_a;
+
+import com.android.tools.r8.resolution.virtualtargets.PackagePrivateFinalOverrideTest.MyViewModel;
+
+public class ViewModelRunnerWithCast {
+
+ public static void run(ViewModel vm) {
+ MyViewModel myViewModel = (MyViewModel) vm;
+ myViewModel.clearBridge(); // <-- will be rewritten to myViewModel.clear()
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
index 663a070..3e47f3b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
@@ -61,4 +61,8 @@
return isCatching(DexItemFactory.throwableDescriptorString);
}
+ @Override
+ public int getNumberOfHandlers() {
+ return tryCatch.guards.size();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
index ed46590..4a96ff1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
@@ -44,4 +44,11 @@
return tryHandler.catchAllAddr != NO_HANDLER;
}
+ @Override
+ public int getNumberOfHandlers() {
+ if (tryHandler.catchAllAddr != NO_HANDLER) {
+ return tryHandler.pairs.length + 1;
+ }
+ return tryHandler.pairs.length;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java
index 838f7a8..73e9ef1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java
@@ -7,4 +7,6 @@
RangeSubject getRange();
boolean isCatching(String exceptionType);
boolean hasCatchAll();
+
+ int getNumberOfHandlers();
}
diff --git a/tools/archive.py b/tools/archive.py
index c24babb..3eb0200 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -8,6 +8,11 @@
import jdk
import optparse
import os
+try:
+ import resource
+except ImportError:
+ # Not a Unix system. Do what Gandalf tells you not to.
+ pass
import resource
import shutil
import subprocess
@@ -92,11 +97,11 @@
return GetStorageDestination('gs://', version_or_path, file_name, is_master)
def GetUrl(version_or_path, file_name, is_master):
- return GetStorageDestination('http://storage.googleapis.com/',
+ return GetStorageDestination('https://storage.googleapis.com/',
version_or_path, file_name, is_master)
def GetMavenUrl(is_master):
- return GetVersionDestination('http://storage.googleapis.com/', '', is_master)
+ return GetVersionDestination('https://storage.googleapis.com/', '', is_master)
def SetRLimitToMax():
(soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
@@ -118,9 +123,10 @@
raise Exception(options.dry_run_output
+ ' does not exist or is not a directory')
- if utils.is_bot():
+ if utils.is_bot() and not utils.IsWindows():
SetRLimitToMax()
- PrintResourceInfo()
+ if not utils.IsWindows():
+ PrintResourceInfo()
# Create maven release which uses a build that exclude dependencies.
create_maven_release.generate_r8_maven_zip(utils.MAVEN_ZIP)
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py
index 295e006..7714362 100755
--- a/tools/archive_desugar_jdk_libs.py
+++ b/tools/archive_desugar_jdk_libs.py
@@ -15,7 +15,7 @@
#
# The first two are the raw jar file and the maven compatible zip file. The
# third is the raw jar file placed and named so that the URL
-# http://storage.googleapis.com/r8-releases/raw can be treated as a maven
+# https://storage.googleapis.com/r8-releases/raw can be treated as a maven
# repository to fetch the artifact com.android.tools:desugar_jdk_libs:1.0.0
import archive
@@ -75,7 +75,7 @@
else:
utils.upload_file_to_cloud_storage(file_name, destination)
print('File available at: %s' %
- destination.replace('gs://', 'http://storage.googleapis.com/', 1))
+ destination.replace('gs://', 'https://storage.googleapis.com/', 1))
def Main(argv):
diff --git a/tools/internal_test.py b/tools/internal_test.py
index 81ccd98..11e4c8b 100755
--- a/tools/internal_test.py
+++ b/tools/internal_test.py
@@ -70,7 +70,7 @@
'find-xmx-min': 256,
'find-xmx-max': 450,
'find-xmx-range': 16,
- 'oom-threshold': 426,
+ 'oom-threshold': 380,
},
{
'app': 'youtube',
diff --git a/tools/r8_release.py b/tools/r8_release.py
index f74fd57..a9e66237 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -285,7 +285,7 @@
def download_file(version, file, dst):
urllib.urlretrieve(
- ('http://storage.googleapis.com/r8-releases/raw/%s/%s' % (version, file)),
+ ('https://storage.googleapis.com/r8-releases/raw/%s/%s' % (version, file)),
dst)
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 8b188f5..cf26883 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -1305,7 +1305,7 @@
if options.hash:
# Download r8-<hash>.jar from
- # http://storage.googleapis.com/r8-releases/raw/.
+ # https://storage.googleapis.com/r8-releases/raw/.
target = 'r8-{}.jar'.format(options.hash)
update_prebuilds_in_android.download_hash(
temp_dir, 'com/android/tools/r8/' + options.hash, target)
@@ -1314,7 +1314,7 @@
quiet=options.quiet)
elif options.version:
# Download r8-<version>.jar from
- # http://storage.googleapis.com/r8-releases/raw/.
+ # https://storage.googleapis.com/r8-releases/raw/.
target = 'r8-{}.jar'.format(options.version)
update_prebuilds_in_android.download_version(
temp_dir, 'com/android/tools/r8/' + options.version, target)
diff --git a/tools/test.py b/tools/test.py
index 9925dea..9359394 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -150,7 +150,7 @@
u_dir = uuid.uuid4()
destination = 'gs://%s/%s' % (BUCKET, u_dir)
utils.upload_dir_to_cloud_storage(upload_dir, destination, is_html=True)
- url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET, u_dir)
+ url = 'https://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET, u_dir)
print 'Test results available at: %s' % url
print '@@@STEP_LINK@Test failures@%s@@@' % url
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index ae1ed2c..f3a0923 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -12,7 +12,7 @@
import utils
import urllib
-BUILD_ROOT = "http://storage.googleapis.com/r8-releases/raw/"
+BUILD_ROOT = "https://storage.googleapis.com/r8-releases/raw/"
MASTER_BUILD_ROOT = "%smaster/" % BUILD_ROOT
JAR_TARGETS_MAP = {