Merge "Allow for emitting tableswitches"
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index d5221b2..6914bc1 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -145,15 +145,14 @@
}
@Override
- public void allocateRegisters(boolean debug) {
- assert options.debug == debug;
- allocateRegisters();
- }
-
public void allocateRegisters() {
computeNeedsRegister();
ImmutableList<BasicBlock> blocks = computeLivenessInformation();
performLinearScan();
+ // Even if the method is reachability sensitive, we do not compute debug information after
+ // register allocation. We just treat the method as being in debug mode in order to keep
+ // locals alive for their entire live range. In release mode the liveness is all that matters
+ // and we do not actually want locals information in the output.
if (options.debug) {
LinearScanRegisterAllocator.computeDebugInfo(blocks, liveIntervals, this, liveAtEntrySets);
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 150e516..fd04436 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -3,16 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+import java.util.ArrayDeque;
import java.util.Collections;
+import java.util.Deque;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
@@ -243,32 +242,19 @@
* <p>The returned set of methods all have {@code callSite.methodName} as the method name.
*
* @param callSite Call site to resolve.
- * @param reporter Reporter used when an unknown metafactory is encountered.
* @return Methods implemented by the lambda expression that created the {@code callSite}.
*/
- public Set<DexEncodedMethod> lookupLambdaImplementedMethods(
- DexCallSite callSite, Reporter reporter) {
+ public Set<DexEncodedMethod> lookupLambdaImplementedMethods(DexCallSite callSite) {
List<DexType> callSiteInterfaces = LambdaDescriptor.getInterfaces(callSite, this);
- if (callSiteInterfaces == null) {
- if (!isStringConcat(callSite.bootstrapMethod)) {
- if (reporter != null) {
- Diagnostic message =
- new StringDiagnostic("Unknown bootstrap method " + callSite.bootstrapMethod);
- reporter.warning(message);
- }
- }
+ if (callSiteInterfaces == null || callSiteInterfaces.isEmpty()) {
return Collections.emptySet();
}
Set<DexEncodedMethod> result = new HashSet<>();
- for (DexType iface : callSiteInterfaces) {
+ Deque<DexType> worklist = new ArrayDeque<>(callSiteInterfaces);
+ Set<DexType> visited = Sets.newIdentityHashSet();
+ while (!worklist.isEmpty()) {
+ DexType iface = worklist.removeFirst();
if (iface.isUnknown()) {
- if (reporter != null) {
- StringDiagnostic message =
- new StringDiagnostic(
- "Lambda expression implements missing library interface "
- + iface.toSourceString());
- reporter.warning(message);
- }
// Skip this interface. If the lambda only implements missing library interfaces and not any
// program interfaces, then minification and tree shaking are not interested in this
// DexCallSite anyway, so skipping this interface is harmless. On the other hand, if
@@ -279,21 +265,26 @@
// anyway.
continue;
}
+ if (!visited.add(iface)) {
+ // Already visited previously. May happen due to "diamond shapes" in the interface
+ // hierarchy.
+ continue;
+ }
assert iface.isInterface();
DexClass clazz = definitionFor(iface);
if (clazz != null) {
- clazz.forEachMethod(
- method -> {
- if (method.method.name == callSite.methodName && method.accessFlags.isAbstract()) {
- result.add(method);
- }
- });
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ if (method.method.name == callSite.methodName && method.accessFlags.isAbstract()) {
+ result.add(method);
+ }
+ }
+ Collections.addAll(worklist, clazz.interfaces.values);
}
}
return result;
}
- private boolean isStringConcat(DexMethodHandle bootstrapMethod) {
+ public boolean isStringConcat(DexMethodHandle bootstrapMethod) {
return bootstrapMethod.type.isInvokeStatic()
&& (bootstrapMethod.asMethod() == dexItemFactory.stringConcatWithConstantsMethod
|| bootstrapMethod.asMethod() == dexItemFactory.stringConcatMethod);
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 4d7a9e1..0474696 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -34,7 +34,6 @@
import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.ir.conversion.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
@@ -669,6 +668,7 @@
new ForwardMethodSourceCode(
accessFlags.isStatic() ? null : holder.type,
newMethod,
+ newMethod,
accessFlags.isStatic() ? null : method.holder,
method,
type,
@@ -847,6 +847,11 @@
}
@Override
+ public boolean isReachabilitySensitive() {
+ return false;
+ }
+
+ @Override
public boolean returnsArgument() {
return false;
}
@@ -956,6 +961,7 @@
// TODO(b/71500340): We call this *hint* because it does not 100% guarantee that a parameter is
// not null when the method returns normally. Maybe nonNullParamOnNormalExit in the future.
private BitSet nonNullParamHints = null;
+ private boolean reachabilitySensitive = false;
private OptimizationInfoImpl() {
// Intentionally left empty, just use the default values.
@@ -977,6 +983,7 @@
initializerEnablingJavaAssertions = template.initializerEnablingJavaAssertions;
parametersUsages = template.parametersUsages;
nonNullParamHints = template.nonNullParamHints;
+ reachabilitySensitive = template.reachabilitySensitive;
}
@Override
@@ -995,6 +1002,11 @@
}
@Override
+ public boolean isReachabilitySensitive() {
+ return reachabilitySensitive;
+ }
+
+ @Override
public boolean returnsArgument() {
return returnedArgument != -1;
}
@@ -1077,6 +1089,11 @@
}
@Override
+ public void setReachabilitySensitive(boolean reachabilitySensitive) {
+ this.reachabilitySensitive = reachabilitySensitive;
+ }
+
+ @Override
public void setClassInlinerEligibility(ClassInlinerEligibility eligibility) {
this.classInlinerEligibility = eligibility;
}
@@ -1186,11 +1203,11 @@
optimizationInfo = info;
}
- public void copyMetadataFromInlinee(DexEncodedMethod inlinee, OptimizationFeedback feedback) {
+ public void copyMetadataFromInlinee(DexEncodedMethod inlinee) {
checkIfObsolete();
// Record that the current method uses identifier name string if the inlinee did so.
if (inlinee.getOptimizationInfo().useIdentifierNameString()) {
- feedback.markUseIdentifierNameString(this);
+ getMutableOptimizationInfo().markUseIdentifierNameString();
}
if (inlinee.classFileVersion > classFileVersion) {
upgradeClassFileVersion(inlinee.getClassFileVersion());
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 6503f11..0450937 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -272,6 +272,8 @@
createType("Ldalvik/annotation/codegen/CovariantReturnType;");
public final DexType annotationCovariantReturnTypes =
createType("Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;");
+ public final DexType annotationReachabilitySensitive =
+ createType("Ldalvik/annotation/optimization/ReachabilitySensitive;");
private static final String METAFACTORY_METHOD_NAME = "metafactory";
private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 1516398..a895aa7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -382,4 +382,44 @@
assert classFileVersion != -1;
return classFileVersion;
}
+
+ /**
+ * Is the class reachability sensitive.
+ *
+ * <p>A class is reachability sensitive if the
+ * dalvik.annotation.optimization.ReachabilitySensitive annotation is on any field or method. When
+ * that is the case, dead reference elimination is disabled and locals are kept alive for their
+ * entire scope.
+ */
+ public boolean hasReachabilitySensitiveAnnotation(DexItemFactory factory) {
+ for (DexEncodedMethod directMethod : directMethods) {
+ for (DexAnnotation annotation : directMethod.annotations.annotations) {
+ if (annotation.annotation.type == factory.annotationReachabilitySensitive) {
+ return true;
+ }
+ }
+ }
+ for (DexEncodedMethod virtualMethod : virtualMethods) {
+ for (DexAnnotation annotation : virtualMethod.annotations.annotations) {
+ if (annotation.annotation.type == factory.annotationReachabilitySensitive) {
+ return true;
+ }
+ }
+ }
+ for (DexEncodedField staticField : staticFields) {
+ for (DexAnnotation annotation : staticField.annotations.annotations) {
+ if (annotation.annotation.type == factory.annotationReachabilitySensitive) {
+ return true;
+ }
+ }
+ }
+ for (DexEncodedField instanceField : instanceFields) {
+ for (DexAnnotation annotation : instanceField.annotations.annotations) {
+ if (annotation.annotation.type == factory.annotationReachabilitySensitive) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index d68cb7d..27e91ef 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.ir.code.Invoke.Type;
import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -84,13 +85,15 @@
public static class Builder {
- protected Builder() {
- }
+ protected Builder() {}
protected final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
protected final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
protected final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
+ private final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
+ private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+
public void map(DexType from, DexType to) {
typeMap.put(from, to);
}
@@ -103,6 +106,16 @@
fieldMap.put(from, to);
}
+ public void move(DexMethod from, DexMethod to) {
+ map(from, to);
+ originalMethodSignatures.put(to, from);
+ }
+
+ public void move(DexField from, DexField to) {
+ fieldMap.put(from, to);
+ originalFieldSignatures.put(to, from);
+ }
+
public GraphLense build(DexItemFactory dexItemFactory) {
return build(dexItemFactory, new IdentityGraphLense());
}
@@ -112,7 +125,13 @@
return previousLense;
}
return new NestedGraphLense(
- typeMap, methodMap, fieldMap, null, null, previousLense, dexItemFactory);
+ typeMap,
+ methodMap,
+ fieldMap,
+ originalFieldSignatures,
+ originalMethodSignatures,
+ previousLense,
+ dexItemFactory);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 99e544a..c2f4943 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -122,11 +122,10 @@
Origin origin) {
assert getOwner() == encodedMethod;
triggerDelayedParsingIfNeccessary();
- return options.debug
+ return options.debug || encodedMethod.getOptimizationInfo().isReachabilitySensitive()
? internalBuildWithLocals(
encodedMethod, encodedMethod, appInfo, graphLense, options, null, null)
- : internalBuild(
- encodedMethod, encodedMethod, appInfo, graphLense, options, null, null);
+ : internalBuild(encodedMethod, encodedMethod, appInfo, graphLense, options, null, null);
}
@Override
@@ -142,7 +141,7 @@
assert getOwner() == encodedMethod;
assert generator != null;
triggerDelayedParsingIfNeccessary();
- return options.debug
+ return options.debug || encodedMethod.getOptimizationInfo().isReachabilitySensitive()
? internalBuildWithLocals(
context, encodedMethod, appInfo, graphLense, options, generator, callerPosition)
: internalBuild(
@@ -176,7 +175,9 @@
InternalOptions options,
ValueNumberGenerator generator,
Position callerPosition) {
- if (!options.debug || !options.proguardConfiguration.getKeepAttributes().localVariableTable) {
+ if (!(encodedMethod.getOptimizationInfo().isReachabilitySensitive()
+ || (options.debug
+ && options.proguardConfiguration.getKeepAttributes().localVariableTable))) {
node.localVariables.clear();
}
diff --git a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
index c693ea4..4fc81d0 100644
--- a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
@@ -21,6 +21,8 @@
BitSet getNonNullParamHints();
+ boolean isReachabilitySensitive();
+
boolean returnsArgument();
int getReturnedArgument();
diff --git a/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java
index d7ae7ae..cb93ee0 100644
--- a/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java
@@ -31,6 +31,8 @@
void setNonNullParamHints(BitSet hints);
+ void setReachabilitySensitive(boolean reachabilitySensitive);
+
void markUseIdentifierNameString();
void markForceInline();
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index e348d10..e11f1bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -635,7 +635,9 @@
private boolean consistentBlockInstructions() {
boolean argumentsAllowed = true;
for (BasicBlock block : blocks) {
- block.consistentBlockInstructions(argumentsAllowed, options.debug);
+ block.consistentBlockInstructions(
+ argumentsAllowed,
+ options.debug || method.getOptimizationInfo().isReachabilitySensitive());
argumentsAllowed = false;
}
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index 190ab3a..7cdcd76 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -63,7 +63,8 @@
@Override
public boolean canBeDeadCode(AppInfo appInfo, IRCode code) {
- return !code.options.debug && code.options.isGeneratingDex();
+ return !(code.options.debug || code.method.getOptimizationInfo().isReachabilitySensitive())
+ && code.options.isGeneratingDex();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index eda63f0..953a7f2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -458,7 +458,7 @@
}
public boolean isDebugMode() {
- return options.debug;
+ return options.debug || method.getOptimizationInfo().isReachabilitySensitive();
}
public Int2ReferenceSortedMap<BlockInfo> getCFG() {
@@ -630,7 +630,7 @@
private boolean insertDebugPositions() {
boolean hasDebugPositions = false;
- if (!options.debug) {
+ if (!isDebugMode()) {
return hasDebugPositions;
}
for (BasicBlock block : blocks) {
@@ -847,7 +847,7 @@
public void addDebugLocalStart(int register, DebugLocalInfo local) {
assert local != null;
- if (!options.debug) {
+ if (!isDebugMode()) {
return;
}
// If the local was not introduced by the previous instruction, start it here.
@@ -867,7 +867,7 @@
public void addDebugLocalEnd(int register, DebugLocalInfo local) {
assert local != null;
- if (!options.debug) {
+ if (!isDebugMode()) {
return;
}
Value value = readRegisterForDebugLocal(register, local);
@@ -877,7 +877,7 @@
}
public void addDebugPosition(Position position) {
- if (options.debug) {
+ if (isDebugMode()) {
assert previousLocalValue == null;
assert source.getCurrentPosition().equals(position);
if (!debugLocalEnds.isEmpty()) {
@@ -1096,7 +1096,7 @@
public void addMove(ValueTypeConstraint constraint, int dest, int src) {
Value in = readRegister(src, constraint);
- if (options.debug) {
+ if (isDebugMode()) {
// If the move is writing to a different local we must construct a new value.
DebugLocalInfo destLocal = getOutgoingLocal(dest);
if (destLocal != null && destLocal != in.getLocalInfo()) {
@@ -1876,7 +1876,7 @@
}
private Value readRegisterForDebugLocal(int register, DebugLocalInfo local) {
- assert options.debug;
+ assert isDebugMode();
ValueTypeConstraint type = ValueTypeConstraint.fromDexType(local.type);
return readRegister(register, type, currentBlock, EdgeType.NON_EDGE, RegisterReadType.DEBUG);
}
@@ -1959,7 +1959,7 @@
}
private DebugLocalInfo getIncomingLocalAtBlock(int register, BasicBlock block) {
- if (options.debug) {
+ if (isDebugMode()) {
int blockOffset = offsets.getInt(block);
return source.getIncomingLocalAtBlock(register, blockOffset);
}
@@ -2055,11 +2055,11 @@
}
private DebugLocalInfo getIncomingLocal(int register) {
- return options.debug ? source.getIncomingLocal(register) : null;
+ return isDebugMode() ? source.getIncomingLocal(register) : null;
}
private DebugLocalInfo getOutgoingLocal(int register) {
- return options.debug ? source.getOutgoingLocal(register) : null;
+ return isDebugMode() ? source.getOutgoingLocal(register) : null;
}
private void checkRegister(int register) {
@@ -2161,7 +2161,7 @@
}
private void attachLocalValues(Instruction ir) {
- if (!options.debug) {
+ if (!isDebugMode()) {
assert previousLocalValue == null;
assert debugLocalEnds.isEmpty();
return;
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 fdc47c0..05fd603 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
@@ -438,18 +438,22 @@
}
private void convertMethodsToDex(DexProgramClass clazz) {
+ boolean isReachabilitySensitive = clazz.hasReachabilitySensitiveAnnotation(options.itemFactory);
// When converting all methods on a class always convert <clinit> first.
for (DexEncodedMethod method : clazz.directMethods()) {
if (method.isClassInitializer()) {
+ method.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive);
convertMethodToDex(method);
break;
}
}
- clazz.forEachMethod(method -> {
- if (!method.isClassInitializer()) {
- convertMethodToDex(method);
- }
- });
+ clazz.forEachMethod(
+ method -> {
+ if (!method.isClassInitializer()) {
+ method.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive);
+ convertMethodToDex(method);
+ }
+ });
}
private void convertMethodToDex(DexEncodedMethod method) {
@@ -478,6 +482,7 @@
public DexApplication optimize(DexApplication application, ExecutorService executorService)
throws ExecutionException {
+ computeReachabilitySensitivity(application);
removeLambdaDeserializationMethods();
collectLambdaMergingCandidates(application);
collectStaticizerCandidates(application);
@@ -566,6 +571,14 @@
return builder.build();
}
+ private void computeReachabilitySensitivity(DexApplication application) {
+ application.classes().forEach(c -> {
+ if (c.hasReachabilitySensitiveAnnotation(options.itemFactory)) {
+ c.methods().forEach(m -> m.getMutableOptimizationInfo().setReachabilitySensitive(true));
+ }
+ });
+ }
+
private void forEachSelectedOutliningMethod(
ExecutorService executorService, BiConsumer<IRCode, DexEncodedMethod> consumer)
throws ExecutionException {
@@ -812,7 +825,9 @@
CodeRewriter.ensureDirectStringNewToInit(code);
}
- if (options.debug) {
+ boolean isDebugMode = options.debug || method.getOptimizationInfo().isReachabilitySensitive();
+
+ if (isDebugMode) {
codeRewriter.simplifyDebugLocals(code);
}
@@ -849,7 +864,7 @@
}
if (identifierNameStringMarker != null) {
- identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(method, code, feedback);
+ identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(method, code);
assert code.isConsistentSSA();
}
@@ -867,10 +882,15 @@
nonNullTracker.addNonNull(code);
assert code.isConsistentSSA();
}
- if (options.enableInlining && inliner != null) {
+ if (!isDebugMode && options.enableInlining && inliner != null) {
// TODO(zerny): Should we support inlining in debug mode? b/62937285
- assert !options.debug;
- inliner.performInlining(method, code, isProcessedConcurrently, callSiteInformation, feedback);
+ inliner.performInlining(method, code, isProcessedConcurrently, callSiteInformation);
+ }
+
+ // Either marked by IdentifierNameStringMarker or propagated from inlinee.
+ // Then, make it visible to IdentifierMinifier.
+ if (method.getOptimizationInfo().useIdentifierNameString()) {
+ feedback.markUseIdentifierNameString(method);
}
if (appInfo.hasLiveness()) {
@@ -878,7 +898,7 @@
codeRewriter.rewriteGetClass(code);
}
- if (!options.debug) {
+ if (!isDebugMode) {
// TODO(jsjeon): Consider merging these into one single optimize().
stringOptimizer.computeTrivialOperationsOnConstString(code, appInfo.dexItemFactory);
// Reflection optimization 2. get*Name() with const-class -> const-string
@@ -920,7 +940,7 @@
assert code.isConsistentSSA();
}
- if (!options.debug) {
+ if (!isDebugMode) {
codeRewriter.collectClassInitializerDefaults(method, code);
}
if (Log.ENABLED) {
@@ -951,12 +971,8 @@
// lambda, it does not get collected by merger.
assert options.enableInlining && inliner != null;
classInliner.processMethodCode(
- appInfo.withLiveness(),
- codeRewriter,
- method,
- code,
- isProcessedConcurrently,
- methodsToInline -> inliner.performForcedInlining(method, code, methodsToInline, feedback),
+ appInfo.withLiveness(), codeRewriter, method, code, isProcessedConcurrently,
+ methodsToInline -> inliner.performForcedInlining(method, code, methodsToInline),
Suppliers.memoize(() -> inliner.createDefaultOracle(
method, code,
isProcessedConcurrently, callSiteInformation,
@@ -1173,7 +1189,7 @@
workaroundForwardingInitializerBug(code);
LinearScanRegisterAllocator registerAllocator =
new LinearScanRegisterAllocator(appInfo, code, options);
- registerAllocator.allocateRegisters(options.debug);
+ registerAllocator.allocateRegisters();
if (options.canHaveExceptionTargetingLoopHeaderBug()) {
codeRewriter.workaroundExceptionTargetingLoopHeaderBug(code);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 0ca4f16..d2a233b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -120,6 +120,7 @@
new ForwardMethodSourceCode(
clazz.type,
newMethod,
+ newMethod,
null /* static method */,
rewriter.defaultAsMethodOfCompanionClass(method),
Invoke.Type.STATIC,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index f963581..955abf0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -170,6 +170,7 @@
new ForwardMethodSourceCode(
clazz.type,
newMethod,
+ newMethod,
method.method.holder,
method.method,
Invoke.Type.VIRTUAL,
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 f989cc9..6ba9b6a 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
@@ -247,7 +247,13 @@
new SynthesizedCode(
callerPosition ->
new ForwardMethodSourceCode(
- null, newMethod, null, origMethod, Type.STATIC, callerPosition)));
+ null,
+ newMethod,
+ newMethod,
+ null,
+ origMethod,
+ Type.STATIC,
+ callerPosition)));
newEncodedMethod.getMutableOptimizationInfo().markNeverInline();
dispatchMethods.add(newEncodedMethod);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index f9e43d7..bad5456 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.CallSiteInformation;
-import com.android.tools.r8.ir.conversion.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -98,6 +97,7 @@
if (target.getOptimizationInfo().forceInline()
|| (inliner.appView.appInfo().hasLiveness()
&& inliner.appView.withLiveness().appInfo().forceInline.contains(target.method))) {
+ assert !appView.appInfo().neverInline.contains(target.method);
return Reason.FORCE;
}
if (inliner.appView.appInfo().hasLiveness()
@@ -343,14 +343,13 @@
}
@Override
- public void ensureMethodProcessed(
- DexEncodedMethod target, IRCode inlinee, OptimizationFeedback feedback) {
+ public void ensureMethodProcessed(DexEncodedMethod target, IRCode inlinee) {
if (!target.isProcessed()) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Forcing extra inline on " + target.toSourceString());
}
inliner.performInlining(
- target, inlinee, isProcessedConcurrently, callSiteInformation, feedback);
+ target, inlinee, isProcessedConcurrently, callSiteInformation);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index 0aaf27f..6890dd8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokePolymorphic;
import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.conversion.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -65,8 +64,7 @@
}
@Override
- public void ensureMethodProcessed(
- DexEncodedMethod target, IRCode inlinee, OptimizationFeedback feedback) {
+ public void ensureMethodProcessed(DexEncodedMethod target, IRCode inlinee) {
// Do nothing. If the method is not yet processed, we still should
// be able to build IR for inlining, though.
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 1368ea3..63d0242 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -543,19 +543,17 @@
public void performForcedInlining(
DexEncodedMethod method,
IRCode code,
- Map<InvokeMethod, InliningInfo> invokesToInline,
- OptimizationFeedback feedback) {
+ Map<InvokeMethod, InliningInfo> invokesToInline) {
ForcedInliningOracle oracle = new ForcedInliningOracle(method, invokesToInline);
- performInliningImpl(oracle, oracle, method, code, feedback);
+ performInliningImpl(oracle, oracle, method, code);
}
public void performInlining(
DexEncodedMethod method,
IRCode code,
Predicate<DexEncodedMethod> isProcessedConcurrently,
- CallSiteInformation callSiteInformation,
- OptimizationFeedback feedback) {
+ CallSiteInformation callSiteInformation) {
DefaultInliningOracle oracle =
createDefaultOracle(
@@ -566,7 +564,7 @@
options.inliningInstructionLimit,
options.inliningInstructionAllowance - numberOfInstructions(code));
- performInliningImpl(oracle, oracle, method, code, feedback);
+ performInliningImpl(oracle, oracle, method, code);
}
public DefaultInliningOracle createDefaultOracle(
@@ -590,11 +588,7 @@
}
private void performInliningImpl(
- InliningStrategy strategy,
- InliningOracle oracle,
- DexEncodedMethod context,
- IRCode code,
- OptimizationFeedback feedback) {
+ InliningStrategy strategy, InliningOracle oracle, DexEncodedMethod context, IRCode code) {
List<BasicBlock> blocksToRemove = new ArrayList<>();
ListIterator<BasicBlock> blockIterator = code.listIterator();
while (blockIterator.hasNext()) {
@@ -631,7 +625,7 @@
// If this code did not go through the full pipeline, apply inlining to make sure
// that force inline targets get processed.
- strategy.ensureMethodProcessed(target, inlinee.code, feedback);
+ strategy.ensureMethodProcessed(target, inlinee.code);
// Make sure constructor inlining is legal.
assert !target.isClassInitializer();
@@ -653,7 +647,7 @@
context.accessFlags.unsetBridge();
}
- context.copyMetadataFromInlinee(target, feedback);
+ context.copyMetadataFromInlinee(target);
code.copyMetadataFromInlinee(inlinee.code);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
index e0ff04b..7dc2b62 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.conversion.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason;
import java.util.ListIterator;
@@ -28,8 +27,7 @@
/** Inform the strategy that the inlinee has been inlined. */
void markInlined(InlineeWithReason inlinee);
- void ensureMethodProcessed(
- DexEncodedMethod target, IRCode inlinee, OptimizationFeedback feedback);
+ void ensureMethodProcessed(DexEncodedMethod target, IRCode inlinee);
boolean isValidTarget(InvokeMethod invoke, DexEncodedMethod target, IRCode inlinee);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index d83c550..eb42723 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.classinliner;
-import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -190,7 +189,7 @@
}
}
- private boolean isClassEligible(AppInfo appInfo, DexClass clazz) {
+ private boolean isClassEligible(AppInfoWithLiveness appInfo, DexClass clazz) {
Boolean eligible = knownClasses.get(clazz);
if (eligible == null) {
Boolean computed = computeClassEligible(appInfo, clazz);
@@ -205,9 +204,12 @@
// - is not an abstract class or interface
// - does not declare finalizer
// - does not trigger any static initializers except for its own
- private boolean computeClassEligible(AppInfo appInfo, DexClass clazz) {
- if (clazz == null || clazz.isLibraryClass() ||
- clazz.accessFlags.isAbstract() || clazz.accessFlags.isInterface()) {
+ private boolean computeClassEligible(AppInfoWithLiveness appInfo, DexClass clazz) {
+ if (clazz == null
+ || clazz.isLibraryClass()
+ || clazz.accessFlags.isAbstract()
+ || clazz.accessFlags.isInterface()
+ || appInfo.neverClassInline.contains(clazz.type)) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 880a674..c971a14 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -199,7 +199,7 @@
* Perform register allocation for the IRCode.
*/
@Override
- public void allocateRegisters(boolean debug) {
+ public void allocateRegisters() {
// There are no linked values prior to register allocation.
assert noLinkedValues();
assert code.isConsistentSSA();
@@ -216,7 +216,11 @@
Log.debug(this.getClass(), toString());
}
assert registersUsed() == 0 || unusedRegisters != null;
- if (debug) {
+ // Even if the method is reachability sensitive, we do not compute debug information after
+ // register allocation. We just treat the method as being in debug mode in order to keep
+ // locals alive for their entire live range. In release mode the liveness is all that matters
+ // and we do not actually want locals information in the output.
+ if (options.debug) {
computeDebugInfo(blocks);
}
clearUserInfo();
@@ -1612,9 +1616,11 @@
// Set all free positions for possible registers to max integer.
RegisterPositions freePositions = new RegisterPositions(registerConstraint + 1);
- if (options.debug && !code.method.accessFlags.isStatic()) {
- // If we are generating debug information, we pin the this value register since the
- // debugger expects to always be able to find it in the input register.
+ if ((options.debug || code.method.getOptimizationInfo().isReachabilitySensitive())
+ && !code.method.accessFlags.isStatic()) {
+ // If we are generating debug information or if the method is reachability sensitive,
+ // we pin the this value register. The debugger expects to always be able to find it in
+ // the input register.
assert numberOfArgumentRegisters > 0;
assert firstArgumentValue != null && firstArgumentValue.requiredRegisters() == 1;
freePositions.set(0, 0);
@@ -2620,7 +2626,9 @@
}
}
}
- if (options.debug) {
+ if (options.debug || code.method.getOptimizationInfo().isReachabilitySensitive()) {
+ // In debug mode, or if the method is reachability sensitive, extend the live range
+ // to cover the full scope of a local variable (encoded as debug values).
int number = instruction.getNumber();
for (Value use : instruction.getDebugValues()) {
assert use.needsRegister();
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
index ba83787..1314b3f 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
@@ -9,7 +9,7 @@
import java.util.List;
public interface RegisterAllocator {
- void allocateRegisters(boolean debug);
+ void allocateRegisters();
int registersUsed();
int getRegisterForValue(Value value, int instructionNumber);
int getArgumentOrAllocateRegisterForValue(Value value, int instructionNumber);
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
index b8e79cd..37341fb 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
@@ -27,22 +27,32 @@
public ForwardMethodSourceCode(
DexType receiver,
DexMethod method,
+ DexMethod originalMethod,
DexType targetReceiver,
DexMethod target,
Type invokeType,
Position callerPosition) {
- this(receiver, method, targetReceiver, target, invokeType, callerPosition, false);
+ this(
+ receiver,
+ method,
+ originalMethod,
+ targetReceiver,
+ target,
+ invokeType,
+ callerPosition,
+ false);
}
public ForwardMethodSourceCode(
DexType receiver,
DexMethod method,
+ DexMethod originalMethod,
DexType targetReceiver,
DexMethod target,
Type invokeType,
Position callerPosition,
boolean castResult) {
- super(receiver, method, callerPosition);
+ super(receiver, method, callerPosition, originalMethod);
assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC);
this.target = target;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index fd04882..88bbc6c 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -51,6 +51,11 @@
private final Position position;
protected SyntheticSourceCode(DexType receiver, DexMethod method, Position callerPosition) {
+ this(receiver, method, callerPosition, method);
+ }
+
+ protected SyntheticSourceCode(
+ DexType receiver, DexMethod method, Position callerPosition, DexMethod originalMethod) {
assert method != null;
this.receiver = receiver;
this.method = method;
@@ -67,7 +72,7 @@
this.paramRegisters[i] = nextRegister(ValueType.fromDexType(params[i]));
}
- position = Position.synthetic(0, method, callerPosition);
+ position = Position.synthetic(0, originalMethod, callerPosition);
}
protected final void add(Consumer<IRBuilder> constructor) {
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 0aff19d..481f6c4 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -32,7 +32,6 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.OptimizationFeedback;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -84,8 +83,7 @@
}
}
- public void decoupleIdentifierNameStringsInMethod(
- DexEncodedMethod encodedMethod, IRCode code, OptimizationFeedback feedback) {
+ public void decoupleIdentifierNameStringsInMethod(DexEncodedMethod encodedMethod, IRCode code) {
if (!code.hasConstString) {
return;
}
@@ -165,7 +163,7 @@
InstancePut instancePut = instruction.asInstancePut();
iterator.replaceCurrentInstruction(new InstancePut(field, instancePut.object(), newIn));
}
- feedback.markUseIdentifierNameString(encodedMethod);
+ encodedMethod.getMutableOptimizationInfo().markUseIdentifierNameString();
} else if (instruction.isInvokeMethod()) {
InvokeMethod invoke = instruction.asInvokeMethod();
DexMethod invokedMethod = invoke.getInvokedMethod();
@@ -280,7 +278,7 @@
invokedMethod.proto,
invoke.outValue(),
newIns));
- feedback.markUseIdentifierNameString(encodedMethod);
+ encodedMethod.getMutableOptimizationInfo().markUseIdentifierNameString();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 6b109c5..df11ba9 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -264,7 +264,7 @@
// Don't report errors, as the set of call sites is a conservative estimate, and can
// refer to interfaces which has been removed.
Set<DexEncodedMethod> implementedMethods =
- appInfo.lookupLambdaImplementedMethods(callSite, null);
+ appInfo.lookupLambdaImplementedMethods(callSite);
if (implementedMethods.isEmpty()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInlineRule.java b/src/main/java/com/android/tools/r8/shaking/ClassInlineRule.java
new file mode 100644
index 0000000..e7dc76c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInlineRule.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.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 ClassInlineRule extends ProguardConfigurationRule {
+
+ public enum Type {
+ NEVER
+ }
+
+ public static class Builder extends ProguardConfigurationRule.Builder<ClassInlineRule, Builder> {
+
+ private Builder() {
+ super();
+ }
+
+ Type type;
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ public Builder setType(Type type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public ClassInlineRule build() {
+ return new ClassInlineRule(
+ origin,
+ getPosition(),
+ source,
+ classAnnotation,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotation,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules,
+ type);
+ }
+ }
+
+ private final Type type;
+
+ protected ClassInlineRule(
+ 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
+ String typeString() {
+ switch (type) {
+ case NEVER:
+ return "neverclassinline";
+ }
+ throw new Unreachable("Unknown class inline type " + type);
+ }
+}
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 06df4a9..fc10fe9 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -44,6 +44,7 @@
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
+import com.android.tools.r8.shaking.RootSetBuilder.IfRuleEvaluator;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -553,9 +554,22 @@
public void registerCallSite(DexCallSite callSite) {
callSites.add(callSite);
super.registerCallSite(callSite);
- for (DexEncodedMethod method :
- appInfo.lookupLambdaImplementedMethods(callSite, options.reporter)) {
- markLambdaInstantiated(method.method.holder, currentMethod);
+
+ List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
+ if (directInterfaces != null) {
+ for (DexType lambdaInstantiatedInterface : directInterfaces) {
+ markLambdaInstantiated(lambdaInstantiatedInterface, currentMethod);
+ }
+ } else {
+ if (!appInfo.isStringConcat(callSite.bootstrapMethod)) {
+ if (options.reporter != null) {
+ Diagnostic message =
+ new StringDiagnostic(
+ "Unknown bootstrap method " + callSite.bootstrapMethod,
+ appInfo.originFor(currentMethod.method.holder));
+ options.reporter.warning(message);
+ }
+ }
}
LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo);
@@ -599,7 +613,6 @@
// and implement all lambda interfaces.
ScopedDexMethodSet seen = new ScopedDexMethodSet();
- List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
if (directInterfaces == null) {
return;
}
@@ -1058,7 +1071,31 @@
}
private void markLambdaInstantiated(DexType itf, DexEncodedMethod method) {
- instantiatedLambdas.add(itf, KeepReason.instantiatedIn(method));
+ DexClass clazz = appInfo.definitionFor(itf);
+ if (clazz == null) {
+ if (options.reporter != null) {
+ StringDiagnostic message =
+ new StringDiagnostic(
+ "Lambda expression implements missing interface `" + itf.toSourceString() + "`",
+ appInfo.originFor(method.method.holder));
+ options.reporter.warning(message);
+ }
+ return;
+ }
+ if (!clazz.isInterface()) {
+ if (options.reporter != null) {
+ StringDiagnostic message =
+ new StringDiagnostic(
+ "Lambda expression expected to implement an interface, but found "
+ + "`" + itf.toSourceString() + "`",
+ appInfo.originFor(method.method.holder));
+ options.reporter.warning(message);
+ }
+ return;
+ }
+ if (clazz.isProgramClass()) {
+ instantiatedLambdas.add(itf, KeepReason.instantiatedIn(method));
+ }
}
private void markDirectStaticOrConstructorMethodAsLive(
@@ -1349,9 +1386,12 @@
if (numOfLiveItemsAfterProcessing > numOfLiveItems) {
RootSetBuilder consequentSetBuilder =
new RootSetBuilder(appView, rootSet.ifRules, options);
- ConsequentRootSet consequentRootSet = consequentSetBuilder.runForIfRules(
- executorService, liveTypes, liveMethods.getItems(), liveFields.getItems());
+ IfRuleEvaluator ifRuleEvaluator =
+ consequentSetBuilder.getIfRuleEvaluator(
+ liveMethods.getItems(), liveFields.getItems(), executorService);
+ ConsequentRootSet consequentRootSet = ifRuleEvaluator.run(liveTypes);
enqueueRootItems(consequentRootSet.noShrinking);
+ rootSet.neverInline.addAll(consequentRootSet.neverInline);
rootSet.noOptimization.addAll(consequentRootSet.noOptimization);
rootSet.noObfuscation.addAll(consequentRootSet.noObfuscation);
rootSet.addDependentItems(consequentRootSet.dependentNoShrinking);
@@ -1839,6 +1879,10 @@
*/
public final Set<DexMethod> neverInline;
/**
+ * All types that *must* never be inlined due to a configuration directive (testing only).
+ */
+ public final Set<DexType> neverClassInline;
+ /**
* All types that *must* never be merged due to a configuration directive (testing only).
*/
public final Set<DexType> neverMerge;
@@ -1903,6 +1947,7 @@
this.alwaysInline = enqueuer.rootSet.alwaysInline;
this.forceInline = enqueuer.rootSet.forceInline;
this.neverInline = enqueuer.rootSet.neverInline;
+ this.neverClassInline = enqueuer.rootSet.neverClassInline;
this.neverMerge = enqueuer.rootSet.neverMerge;
this.identifierNameStrings = joinIdentifierNameStrings(
enqueuer.rootSet.identifierNameStrings, enqueuer.identifierNameStrings);
@@ -1947,6 +1992,7 @@
this.alwaysInline = previous.alwaysInline;
this.forceInline = previous.forceInline;
this.neverInline = previous.neverInline;
+ this.neverClassInline = previous.neverClassInline;
this.neverMerge = previous.neverMerge;
this.identifierNameStrings = previous.identifierNameStrings;
this.prunedTypes = mergeSets(previous.prunedTypes, removedClasses);
@@ -2005,6 +2051,7 @@
assert lense.assertDefinitionNotModified(
previous.neverMerge.stream().map(this::definitionFor).filter(Objects::nonNull)
.collect(Collectors.toList()));
+ this.neverClassInline = rewriteItems(previous.neverClassInline, lense::lookupType);
this.neverMerge = previous.neverMerge;
this.identifierNameStrings =
lense.rewriteReferencesConservatively(previous.identifierNameStrings);
@@ -2050,6 +2097,7 @@
this.alwaysInline = previous.alwaysInline;
this.forceInline = previous.forceInline;
this.neverInline = previous.neverInline;
+ this.neverClassInline = previous.neverClassInline;
this.neverMerge = previous.neverMerge;
this.identifierNameStrings = previous.identifierNameStrings;
this.prunedTypes = previous.prunedTypes;
@@ -2419,6 +2467,9 @@
: Iterables.concat(
ImmutableList.of(refinedReceiverType), subtypes(refinedReceiverType));
for (DexType type : subTypesToExplore) {
+ if (instantiatedLambdas.contains(type)) {
+ return null;
+ }
if (pinnedItems.contains(type)) {
// For kept classes we cannot ensure a single target.
return null;
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
index f263bd6..3f255f2 100644
--- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -42,7 +42,7 @@
private final Type type;
- private InlineRule(
+ protected InlineRule(
Origin origin,
Position position,
String source,
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 ea20704..2ab9308 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.position.Position;
import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.position.TextRange;
-import com.android.tools.r8.shaking.InlineRule.Type;
import com.android.tools.r8.shaking.ProguardConfiguration.Builder;
import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
@@ -353,16 +352,19 @@
} else if (acceptString("packageobfuscationdictionary")) {
configurationBuilder.setPackageObfuscationDictionary(parseFileName(false));
} else if (acceptString("alwaysinline")) {
- InlineRule rule = parseInlineRule(Type.ALWAYS, optionStart);
+ InlineRule rule = parseInlineRule(InlineRule.Type.ALWAYS, optionStart);
configurationBuilder.addRule(rule);
} else if (allowTestOptions && acceptString("forceinline")) {
- InlineRule rule = parseInlineRule(Type.FORCE, optionStart);
+ InlineRule rule = parseInlineRule(InlineRule.Type.FORCE, optionStart);
configurationBuilder.addRule(rule);
// Insert a matching -checkdiscard rule to ensure force inlining happens.
ProguardCheckDiscardRule ruled = rule.asProguardCheckDiscardRule();
configurationBuilder.addRule(ruled);
} else if (allowTestOptions && acceptString("neverinline")) {
- InlineRule rule = parseInlineRule(Type.NEVER, optionStart);
+ InlineRule rule = parseInlineRule(InlineRule.Type.NEVER, optionStart);
+ configurationBuilder.addRule(rule);
+ } else if (allowTestOptions && acceptString("neverclassinline")) {
+ ClassInlineRule rule = parseClassInlineRule(ClassInlineRule.Type.NEVER, optionStart);
configurationBuilder.addRule(rule);
} else if (allowTestOptions && acceptString("nevermerge")) {
ClassMergingRule rule = parseClassMergingRule(ClassMergingRule.Type.NEVER, optionStart);
@@ -597,6 +599,17 @@
return keepRuleBuilder.build();
}
+ private ClassInlineRule parseClassInlineRule(ClassInlineRule.Type type, Position start)
+ throws ProguardRuleParserException {
+ ClassInlineRule.Builder keepRuleBuilder =
+ ClassInlineRule.builder().setOrigin(origin).setStart(start).setType(type);
+ parseClassSpec(keepRuleBuilder, false);
+ Position end = getPosition();
+ keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+ keepRuleBuilder.setEnd(end);
+ return keepRuleBuilder.build();
+ }
+
private ClassMergingRule parseClassMergingRule(ClassMergingRule.Type type, Position start)
throws ProguardRuleParserException {
ClassMergingRule.Builder keepRuleBuilder =
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 6d6d943..3011951 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -87,6 +87,52 @@
subsequentRule.materialize());
}
+ /**
+ * Consider the following rule, which requests that class Y should be kept if the method X.m() is
+ * in the final output.
+ *
+ * <pre>
+ * -if class X {
+ * public void m();
+ * }
+ * -keep class Y
+ * </pre>
+ *
+ * When the {@link Enqueuer} finds that the method X.m() is reachable, it applies the subsequent
+ * keep rule of the -if rule. Thus, Y will be marked as pinned, which guarantees, for example,
+ * that it will not be merged into another class by the vertical class merger.
+ *
+ * <p>However, when the {@link Enqueuer} runs for the second time, it is important that X.m() has
+ * not been inlined into another method Z.z(), because that would mean that Z.z() now relies on
+ * the presence of Y, meanwhile Y will not be kept because X.m() is no longer present.
+ *
+ * <p>Therefore, each time the subsequent rule of an -if rule is applied, we also apply a
+ * -neverinline rule for the condition of the -if rule.
+ */
+ protected InlineRule neverInlineRuleForCondition() {
+ if (getMemberRules() == null || getMemberRules().isEmpty()) {
+ return null;
+ }
+ return new InlineRule(
+ Origin.unknown(),
+ Position.UNKNOWN,
+ null,
+ getClassAnnotation() == null ? null : getClassAnnotation().materialize(),
+ getClassAccessFlags(),
+ getNegatedClassAccessFlags(),
+ getClassTypeNegated(),
+ getClassType(),
+ getClassNames().materialize(),
+ getInheritanceAnnotation() == null ? null : getInheritanceAnnotation().materialize(),
+ getInheritanceClassName() == null ? null : getInheritanceClassName().materialize(),
+ getInheritanceIsExtends(),
+ getMemberRules().stream()
+ .filter(rule -> rule.getRuleType().includesMethods())
+ .map(ProguardMemberRule::materialize)
+ .collect(Collectors.toList()),
+ InlineRule.Type.NEVER);
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof ProguardIfRule)) {
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 df72e96..c59081e 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -66,6 +66,7 @@
private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
private final Set<DexMethod> forceInline = Sets.newIdentityHashSet();
private final Set<DexMethod> neverInline = Sets.newIdentityHashSet();
+ private final Set<DexType> neverClassInline = Sets.newIdentityHashSet();
private final Set<DexType> neverMerge = Sets.newIdentityHashSet();
private final Map<DexDefinition, Map<DexDefinition, ProguardKeepRule>> dependentNoShrinking =
new IdentityHashMap<>();
@@ -180,6 +181,10 @@
}
} else if (rule instanceof InlineRule) {
markMatchingMethods(clazz, memberKeepRules, rule, null);
+ } else if (rule instanceof ClassInlineRule) {
+ if (allRulesSatisfied(memberKeepRules, clazz)) {
+ markClass(clazz, rule);
+ }
} else if (rule instanceof ProguardAssumeValuesRule) {
markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
markMatchingVisibleFields(clazz, memberKeepRules, rule, null);
@@ -250,6 +255,7 @@
alwaysInline,
forceInline,
neverInline,
+ neverClassInline,
neverMerge,
noSideEffects,
assumedValues,
@@ -258,55 +264,15 @@
ifRules);
}
- ConsequentRootSet runForIfRules(
- ExecutorService executorService,
- Set<DexType> liveTypes,
+ IfRuleEvaluator getIfRuleEvaluator(
Set<DexEncodedMethod> liveMethods,
- Set<DexEncodedField> liveFields) throws ExecutionException {
- application.timing.begin("Find consequent items for -if rules...");
- try {
- if (rules != null) {
- IfRuleEvaluator evaluator =
- new IfRuleEvaluator(liveTypes, liveMethods, liveFields, executorService);
- for (ProguardConfigurationRule rule : rules) {
- assert rule instanceof ProguardIfRule;
- ProguardIfRule ifRule = (ProguardIfRule) rule;
- // Depending on which types that trigger the -if rule, the application of the subsequent
- // -keep rule may vary (due to back references). So, we need to try all pairs of -if rule
- // and live types.
- for (DexType type : liveTypes) {
- DexClass clazz = appView.appInfo().definitionFor(type);
- if (clazz == null) {
- continue;
- }
-
- // Check if the class matches the if-rule.
- evaluator.evaluateIfRule(ifRule, clazz, clazz);
-
- // Check if one of the types that have been merged into `clazz` satisfies the if-rule.
- if (options.enableVerticalClassMerging && appView.verticallyMergedClasses() != null) {
- for (DexType sourceType : appView.verticallyMergedClasses().getSourcesFor(type)) {
- // Note that, although `sourceType` has been merged into `type`, the dex class for
- // `sourceType` is still available until the second round of tree shaking. This way
- // we can still retrieve the access flags of `sourceType`.
- DexClass sourceClass = appView.appInfo().definitionFor(sourceType);
- assert sourceClass != null;
- evaluator.evaluateIfRule(ifRule, sourceClass, clazz);
- }
- }
- }
- }
- ThreadUtils.awaitFutures(evaluator.futures);
- }
- } finally {
- application.timing.end();
- }
- return new ConsequentRootSet(noShrinking, noOptimization, noObfuscation, dependentNoShrinking);
+ Set<DexEncodedField> liveFields,
+ ExecutorService executorService) {
+ return new IfRuleEvaluator(liveMethods, liveFields, executorService);
}
- private class IfRuleEvaluator {
+ class IfRuleEvaluator {
- private final Set<DexType> liveTypes;
private final Set<DexEncodedMethod> liveMethods;
private final Set<DexEncodedField> liveFields;
private final ExecutorService executorService;
@@ -314,16 +280,56 @@
private final List<Future<?>> futures = new ArrayList<>();
public IfRuleEvaluator(
- Set<DexType> liveTypes,
Set<DexEncodedMethod> liveMethods,
Set<DexEncodedField> liveFields,
ExecutorService executorService) {
- this.liveTypes = liveTypes;
this.liveMethods = liveMethods;
this.liveFields = liveFields;
this.executorService = executorService;
}
+ public ConsequentRootSet run(Set<DexType> liveTypes) throws ExecutionException {
+ application.timing.begin("Find consequent items for -if rules...");
+ try {
+ if (rules != null) {
+ for (ProguardConfigurationRule rule : rules) {
+ assert rule instanceof ProguardIfRule;
+ ProguardIfRule ifRule = (ProguardIfRule) rule;
+ // Depending on which types that trigger the -if rule, the application of the subsequent
+ // -keep rule may vary (due to back references). So, we need to try all pairs of -if
+ // rule and live types.
+ for (DexType type : liveTypes) {
+ DexClass clazz = appView.appInfo().definitionFor(type);
+ if (clazz == null) {
+ continue;
+ }
+
+ // Check if the class matches the if-rule.
+ evaluateIfRule(ifRule, clazz, clazz);
+
+ // Check if one of the types that have been merged into `clazz` satisfies the if-rule.
+ if (options.enableVerticalClassMerging && appView.verticallyMergedClasses() != null) {
+ for (DexType sourceType : appView.verticallyMergedClasses().getSourcesFor(type)) {
+ // Note that, although `sourceType` has been merged into `type`, the dex class for
+ // `sourceType` is still available until the second round of tree shaking. This
+ // way
+ // we can still retrieve the access flags of `sourceType`.
+ DexClass sourceClass = appView.appInfo().definitionFor(sourceType);
+ assert sourceClass != null;
+ evaluateIfRule(ifRule, sourceClass, clazz);
+ }
+ }
+ }
+ }
+ ThreadUtils.awaitFutures(futures);
+ }
+ } finally {
+ application.timing.end();
+ }
+ return new ConsequentRootSet(
+ neverInline, noShrinking, noOptimization, noObfuscation, dependentNoShrinking);
+ }
+
/**
* Determines if `sourceClass` satisfies the given if-rule. If `sourceClass` has not been merged
* into another class, then `targetClass` is the same as `sourceClass`. Otherwise, `targetClass`
@@ -406,6 +412,16 @@
private void materializeIfRule(ProguardIfRule rule) {
ProguardIfRule materializedRule = rule.materialize();
+
+ // If the condition of the -if rule has any members, then we need to keep these members to
+ // ensure that the subsequent rule will be applied again in the second round of tree
+ // shaking.
+ InlineRule neverInlineRuleForCondition = materializedRule.neverInlineRuleForCondition();
+ if (neverInlineRuleForCondition != null) {
+ runPerRule(executorService, futures, neverInlineRuleForCondition, materializedRule);
+ }
+
+ // Keep whatever is required by the -if rule.
runPerRule(executorService, futures, materializedRule.subsequentRule, materializedRule);
}
}
@@ -852,20 +868,26 @@
} else if (context instanceof ProguardCheckDiscardRule) {
checkDiscarded.add(item);
} else if (context instanceof InlineRule) {
- switch (((InlineRule) context).getType()) {
- case ALWAYS:
- if (item.isDexEncodedMethod()) {
+ if (item.isDexEncodedMethod()) {
+ switch (((InlineRule) context).getType()) {
+ case ALWAYS:
alwaysInline.add(item.asDexEncodedMethod().method);
- }
- break;
- case FORCE:
- if (item.isDexEncodedMethod()) {
+ break;
+ case FORCE:
forceInline.add(item.asDexEncodedMethod().method);
- }
- break;
- case NEVER:
- if (item.isDexEncodedMethod()) {
+ break;
+ case NEVER:
neverInline.add(item.asDexEncodedMethod().method);
+ break;
+ default:
+ throw new Unreachable();
+ }
+ }
+ } else if (context instanceof ClassInlineRule) {
+ switch (((ClassInlineRule) context).getType()) {
+ case NEVER:
+ if (item.isDexClass()) {
+ neverClassInline.add(item.asDexClass().type);
}
break;
default:
@@ -901,6 +923,7 @@
public final Set<DexMethod> alwaysInline;
public final Set<DexMethod> forceInline;
public final Set<DexMethod> neverInline;
+ public final Set<DexType> neverClassInline;
public final Set<DexType> neverMerge;
public final Map<DexDefinition, ProguardMemberRule> noSideEffects;
public final Map<DexDefinition, ProguardMemberRule> assumedValues;
@@ -918,6 +941,7 @@
Set<DexMethod> alwaysInline,
Set<DexMethod> forceInline,
Set<DexMethod> neverInline,
+ Set<DexType> neverClassInline,
Set<DexType> neverMerge,
Map<DexDefinition, ProguardMemberRule> noSideEffects,
Map<DexDefinition, ProguardMemberRule> assumedValues,
@@ -932,7 +956,8 @@
this.checkDiscarded = Collections.unmodifiableSet(checkDiscarded);
this.alwaysInline = Collections.unmodifiableSet(alwaysInline);
this.forceInline = Collections.unmodifiableSet(forceInline);
- this.neverInline = Collections.unmodifiableSet(neverInline);
+ this.neverInline = neverInline;
+ this.neverClassInline = Collections.unmodifiableSet(neverClassInline);
this.neverMerge = Collections.unmodifiableSet(neverMerge);
this.noSideEffects = Collections.unmodifiableMap(noSideEffects);
this.assumedValues = Collections.unmodifiableMap(assumedValues);
@@ -984,16 +1009,19 @@
// A partial RootSet that becomes live due to the enabled -if rule.
static class ConsequentRootSet {
+ final Set<DexMethod> neverInline;
final Map<DexDefinition, ProguardKeepRule> noShrinking;
final Set<DexDefinition> noOptimization;
final Set<DexDefinition> noObfuscation;
final Map<DexDefinition, Map<DexDefinition, ProguardKeepRule>> dependentNoShrinking;
private ConsequentRootSet(
+ Set<DexMethod> neverInline,
Map<DexDefinition, ProguardKeepRule> noShrinking,
Set<DexDefinition> noOptimization,
Set<DexDefinition> noObfuscation,
Map<DexDefinition, Map<DexDefinition, ProguardKeepRule>> dependentNoShrinking) {
+ this.neverInline = Collections.unmodifiableSet(neverInline);
this.noShrinking = Collections.unmodifiableMap(noShrinking);
this.noOptimization = Collections.unmodifiableSet(noOptimization);
this.noObfuscation = Collections.unmodifiableSet(noObfuscation);
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index b9b9a99..4dfee8e 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -60,6 +60,7 @@
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -613,9 +614,28 @@
assert result.assertDefinitionNotModified(appInfo.noSideEffects.keySet());
// TODO(christofferqa): Enable this assert.
// assert result.assertNotModified(appInfo.pinnedItems);
+ assert verifyGraphLense(graphLense);
return result;
}
+ private boolean verifyGraphLense(GraphLense graphLense) {
+ for (DexProgramClass clazz : appInfo.classes()) {
+ for (DexEncodedMethod encodedMethod : clazz.methods()) {
+ DexMethod method = encodedMethod.method;
+ DexMethod originalMethod = graphLense.getOriginalMethodSignature(method);
+
+ // Must be able to map back.
+ assert method == graphLense.getRenamedMethodSignature(originalMethod);
+
+ // Verify that all types are up-to-date. After vertical class merging, there should be no
+ // more references to types that have been merged into another type.
+ assert !mergedClasses.containsKey(method.proto.returnType);
+ assert Arrays.stream(method.proto.parameters.values).noneMatch(mergedClasses::containsKey);
+ }
+ }
+ return true;
+ }
+
private GraphLense mergeClasses(GraphLense graphLense) {
// Visit the program classes in a top-down order according to the class hierarchy.
TopDownClassHierarchyTraversal.visit(appView, mergeCandidates, this::mergeClassIfPossible);
@@ -900,6 +920,7 @@
// it turns out that the method is never used, it will be removed by the final round
// of tree shaking.
shadowedBy = buildBridgeMethod(virtualMethod, resultingDirectMethod);
+ deferredRenamings.recordCreationOfBridgeMethod(virtualMethod.method, shadowedBy.method);
add(virtualMethods, shadowedBy, MethodSignatureEquivalence.get());
}
@@ -1109,6 +1130,7 @@
SynthesizedBridgeCode code =
new SynthesizedBridgeCode(
newMethod,
+ graphLense.getOriginalMethodSignature(method.method),
invocationTarget.method,
invocationTarget.isPrivateMethod() ? DIRECT : STATIC);
@@ -1393,7 +1415,7 @@
DexMethod newMethod = application.dexItemFactory.createMethod(newHolder, newProto,
method.name);
if (newMethod != encodedMethod.method) {
- lense.map(encodedMethod.method, newMethod);
+ lense.move(encodedMethod.method, newMethod);
methods[i] = encodedMethod.toTypeSubstitutedMethod(newMethod);
}
}
@@ -1411,7 +1433,7 @@
DexType newHolder = fixupType(field.clazz);
DexField newField = application.dexItemFactory.createField(newHolder, newType, field.name);
if (newField != encodedField.field) {
- lense.map(encodedField.field, newField);
+ lense.move(encodedField.field, newField);
fields[i] = encodedField.toTypeSubstitutedField(newField);
}
}
@@ -1801,11 +1823,14 @@
protected static class SynthesizedBridgeCode extends AbstractSynthesizedCode {
private DexMethod method;
+ private DexMethod originalMethod;
private DexMethod invocationTarget;
private Type type;
- public SynthesizedBridgeCode(DexMethod method, DexMethod invocationTarget, Type type) {
+ public SynthesizedBridgeCode(
+ DexMethod method, DexMethod originalMethod, DexMethod invocationTarget, Type type) {
this.method = method;
+ this.originalMethod = originalMethod;
this.invocationTarget = invocationTarget;
this.type = type;
}
@@ -1832,6 +1857,7 @@
new ForwardMethodSourceCode(
method.holder,
method,
+ originalMethod,
type == DIRECT ? method.holder : null,
invocationTarget,
type,
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
index 43c5e92..6b66465 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -17,10 +17,10 @@
import com.android.tools.r8.shaking.VerticalClassMerger.SynthesizedBridgeCode;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
-import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.HashMap;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -53,9 +53,10 @@
public class VerticalClassMergerGraphLense extends NestedGraphLense {
private final AppInfo appInfo;
- private final Set<DexMethod> mergedMethods;
private final Map<DexType, Map<DexMethod, GraphLenseLookupResult>>
contextualVirtualToDirectMethodMaps;
+ private final Set<DexMethod> mergedMethods;
+ private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges;
public VerticalClassMergerGraphLense(
AppInfo appInfo,
@@ -65,6 +66,7 @@
Map<DexType, Map<DexMethod, GraphLenseLookupResult>> contextualVirtualToDirectMethodMaps,
BiMap<DexField, DexField> originalFieldSignatures,
BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ Map<DexMethod, DexMethod> originalMethodSignaturesForBridges,
GraphLense previousLense) {
super(
ImmutableMap.of(),
@@ -75,8 +77,15 @@
previousLense,
appInfo.dexItemFactory);
this.appInfo = appInfo;
- this.mergedMethods = mergedMethods;
this.contextualVirtualToDirectMethodMaps = contextualVirtualToDirectMethodMaps;
+ this.mergedMethods = mergedMethods;
+ this.originalMethodSignaturesForBridges = originalMethodSignaturesForBridges;
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ return super.getOriginalMethodSignature(
+ originalMethodSignaturesForBridges.getOrDefault(method, method));
}
@Override
@@ -154,7 +163,9 @@
private final Map<DexType, Map<DexMethod, GraphLenseLookupResult>>
contextualVirtualToDirectMethodMaps = new HashMap<>();
- private final Map<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+ private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+ private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges =
+ new IdentityHashMap<>();
public GraphLense build(
GraphLense previousLense,
@@ -184,10 +195,9 @@
getMergedMethodSignaturesAfterClassMerging(
mergedMethodsBuilder.build(), mergedClasses, appInfo.dexItemFactory, cache),
contextualVirtualToDirectMethodMaps,
- getOriginalFieldSignaturesAfterClassMerging(
- originalFieldSignatures, mergedClasses, appInfo.dexItemFactory),
- getOriginalMethodSignaturesAfterClassMerging(
- originalMethodSignatures, mergedClasses, appInfo.dexItemFactory, cache),
+ originalFieldSignatures,
+ originalMethodSignatures,
+ originalMethodSignaturesForBridges,
previousLense);
}
@@ -207,44 +217,6 @@
return result.build();
}
- private static BiMap<DexField, DexField> getOriginalFieldSignaturesAfterClassMerging(
- Map<DexField, DexField> originalFieldSignatures,
- Map<DexType, DexType> mergedClasses,
- DexItemFactory dexItemFactory) {
- ImmutableBiMap.Builder<DexField, DexField> result = ImmutableBiMap.builder();
- for (Map.Entry<DexField, DexField> entry : originalFieldSignatures.entrySet()) {
- result.put(
- getFieldSignatureAfterClassMerging(entry.getKey(), mergedClasses, dexItemFactory),
- entry.getValue());
- }
- return result.build();
- }
-
- private static BiMap<DexMethod, DexMethod> getOriginalMethodSignaturesAfterClassMerging(
- Map<DexMethod, DexMethod> originalMethodSignatures,
- Map<DexType, DexType> mergedClasses,
- DexItemFactory dexItemFactory,
- Map<DexProto, DexProto> cache) {
- ImmutableBiMap.Builder<DexMethod, DexMethod> result = ImmutableBiMap.builder();
- for (Map.Entry<DexMethod, DexMethod> entry : originalMethodSignatures.entrySet()) {
- result.put(
- getMethodSignatureAfterClassMerging(
- entry.getKey(), mergedClasses, dexItemFactory, cache),
- entry.getValue());
- }
- return result.build();
- }
-
- private static DexField getFieldSignatureAfterClassMerging(
- DexField signature, Map<DexType, DexType> mergedClasses, DexItemFactory dexItemFactory) {
- DexType newClass = mergedClasses.getOrDefault(signature.clazz, signature.clazz);
- DexType newType = mergedClasses.getOrDefault(signature.type, signature.type);
- if (signature.clazz.equals(newClass) && signature.type.equals(newType)) {
- return signature;
- }
- return dexItemFactory.createField(newClass, newType, signature.name);
- }
-
private static DexMethod getMethodSignatureAfterClassMerging(
DexMethod signature,
Map<DexType, DexType> mergedClasses,
@@ -285,6 +257,10 @@
originalMethodSignatures.put(to, from);
}
+ public void recordCreationOfBridgeMethod(DexMethod from, DexMethod to) {
+ originalMethodSignaturesForBridges.put(to, from);
+ }
+
public void mapVirtualMethodToDirectInType(
DexMethod from, GraphLenseLookupResult to, DexType type) {
Map<DexMethod, GraphLenseLookupResult> virtualToDirectMethodMap =
@@ -297,6 +273,7 @@
methodMap.putAll(builder.methodMap);
mergedMethodsBuilder.addAll(builder.mergedMethodsBuilder.build());
originalMethodSignatures.putAll(builder.originalMethodSignatures);
+ originalMethodSignaturesForBridges.putAll(builder.originalMethodSignaturesForBridges);
for (DexType context : builder.contextualVirtualToDirectMethodMaps.keySet()) {
Map<DexMethod, GraphLenseLookupResult> current =
contextualVirtualToDirectMethodMaps.get(context);
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanUtils.java b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
new file mode 100644
index 0000000..bacea6f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+public class BooleanUtils {
+
+ private static final Boolean[] VALUES = new Boolean[] { Boolean.TRUE, Boolean.FALSE };
+
+ public static Boolean[] values() {
+ return VALUES;
+ }
+}
diff --git a/src/test/examples/inlining/AlwaysInline.java b/src/test/examples/inlining/AlwaysInline.java
index 815d50a..22cb31e 100644
--- a/src/test/examples/inlining/AlwaysInline.java
+++ b/src/test/examples/inlining/AlwaysInline.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package inlining;
-public @interface AlwaysInline {
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
-}
+@Target({ElementType.METHOD})
+public @interface AlwaysInline {}
diff --git a/src/test/examples/inlining/NeverInline.java b/src/test/examples/inlining/NeverInline.java
index 0db5239..0c5b581 100644
--- a/src/test/examples/inlining/NeverInline.java
+++ b/src/test/examples/inlining/NeverInline.java
@@ -3,4 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package inlining;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
public @interface NeverInline {}
diff --git a/src/test/java/com/android/tools/r8/NeverClassInline.java b/src/test/java/com/android/tools/r8/NeverClassInline.java
new file mode 100644
index 0000000..aa7f9b5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NeverClassInline.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+public @interface NeverClassInline {}
diff --git a/src/test/java/com/android/tools/r8/NeverMerge.java b/src/test/java/com/android/tools/r8/NeverMerge.java
index 7fc97b9..7c6922a 100644
--- a/src/test/java/com/android/tools/r8/NeverMerge.java
+++ b/src/test/java/com/android/tools/r8/NeverMerge.java
@@ -3,4 +3,8 @@
// 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 NeverMerge {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 3a249e6..5550415 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -30,6 +30,7 @@
}
private boolean enableInliningAnnotations = false;
+ private boolean enableClassInliningAnnotations = false;
private boolean enableMergeAnnotations = false;
@Override
@@ -41,7 +42,7 @@
R8TestCompileResult internalCompile(
Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
throws CompilationFailedException {
- if (enableInliningAnnotations || enableMergeAnnotations) {
+ if (enableInliningAnnotations || enableClassInliningAnnotations || enableMergeAnnotations) {
ToolHelper.allowTestProguardOptions(builder);
}
StringBuilder proguardMapBuilder = new StringBuilder();
@@ -71,6 +72,14 @@
return self();
}
+ public R8TestBuilder enableClassInliningAnnotations() {
+ if (!enableClassInliningAnnotations) {
+ enableClassInliningAnnotations = true;
+ addKeepRules("-neverclassinline @com.android.tools.r8.NeverClassInline class *");
+ }
+ return self();
+ }
+
public R8TestBuilder enableMergeAnnotations() {
if (!enableMergeAnnotations) {
enableMergeAnnotations = true;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index d6e7c0d..f968b8c 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -143,6 +143,10 @@
this.shortName = shortName;
}
+ public boolean isLatest() {
+ return this == DEFAULT;
+ }
+
public boolean isNewerThan(Version other) {
return compareTo(other) > 0;
}
diff --git a/src/test/java/com/android/tools/r8/d8/IncompatiblePrimitiveTypesTest.java b/src/test/java/com/android/tools/r8/d8/IncompatiblePrimitiveTypesTest.java
new file mode 100644
index 0000000..010f903
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/d8/IncompatiblePrimitiveTypesTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.d8;
+
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import org.hamcrest.Matcher;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class IncompatiblePrimitiveTypesTest extends TestBase {
+
+ @ClassRule
+ public static TemporaryFolder tempFolder = ToolHelper.getTemporaryFolderForTest();
+
+ private static Path inputJar;
+ private static String expectedOutput = "true";
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ JasminBuilder jasminBuilder = new JasminBuilder();
+ ClassBuilder classBuilder = jasminBuilder.addClass("TestClass");
+ classBuilder
+ .staticMethodBuilder("main", ImmutableList.of("[Ljava/lang/String;"), "V")
+ .setCode(
+ "invokestatic TestClass/getByte()B", "invokestatic TestClass/takeBoolean(Z)V", "return")
+ .build();
+ classBuilder
+ .staticMethodBuilder("getByte", ImmutableList.of(), "B")
+ .setCode("iconst_1", "ireturn")
+ .build();
+ classBuilder
+ .staticMethodBuilder("takeBoolean", ImmutableList.of("Z"), "V")
+ .setCode(
+ "getstatic java/lang/System/out Ljava/io/PrintStream;",
+ "iload_0",
+ "invokevirtual java/io/PrintStream/print(Z)V",
+ "return")
+ .build();
+ inputJar = tempFolder.getRoot().toPath().resolve("input.jar");
+ jasminBuilder.writeJar(inputJar);
+ }
+
+ @Test
+ public void jvmTest() throws Exception {
+ assumeTrue(
+ "JVM test independent of Art version - only run when testing on latest",
+ ToolHelper.getDexVm().getVersion().isLatest());
+ testForJvm().addClasspath(inputJar).run("TestClass").assertSuccessWithOutput(expectedOutput);
+ }
+
+ @Test
+ public void dexTest() throws Exception {
+ TestRunResult d8Result = testForD8().addProgramFiles(inputJar).run("TestClass");
+ TestRunResult dxResult = testForDX().addProgramFiles(inputJar).run("TestClass");
+ if (ToolHelper.getDexVm().getVersion().isNewerThan(Version.V4_4_4)) {
+ d8Result.assertSuccessWithOutput(expectedOutput);
+ dxResult.assertSuccessWithOutput(expectedOutput);
+ } else {
+ // TODO(b/119812046): On Art 4.0.4 and 4.4.4 it is a verification error to use one short type
+ // as another short type.
+ Matcher<String> expectedError = containsString("java.lang.VerifyError");
+ d8Result.assertFailureWithErrorThatMatches(expectedError);
+ dxResult.assertFailureWithErrorThatMatches(expectedError);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/B116282409.java b/src/test/java/com/android/tools/r8/ir/optimize/B116282409.java
index 6fe1f16..6008aab 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/B116282409.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/B116282409.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.jasmin.JasminTestBase;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.Collection;
@@ -44,11 +45,7 @@
@Parameters(name = "Backend: {0}, vertical class merging: {1}")
public static Collection<Object[]> data() {
- return ImmutableList.of(
- new Object[] {Backend.CF, false},
- new Object[] {Backend.CF, true},
- new Object[] {Backend.DEX, false},
- new Object[] {Backend.DEX, true});
+ return buildParameters(Backend.values(), BooleanUtils.values());
}
public B116282409(Backend backend, boolean enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
index 0572c36..55c64ee 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
@@ -128,6 +128,7 @@
blocks.add(block);
InternalOptions options = new InternalOptions();
+ options.debug = true;
AppInfo appInfo = new AppInfo(DexApplication.builder(options.itemFactory, null).build());
IRCode code =
new IRCode(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index e1167b9..c9e5f1e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -27,7 +28,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -49,15 +49,11 @@
@Parameters(name = "{0}, backend={1}, minification={2}, allowaccessmodification={3}")
public static Collection<Object[]> data() {
- List<Object[]> configs = new ArrayList<>();
- for (Backend backend : Backend.values()) {
- for (boolean minification : new Boolean[] {false, true}) {
- for (boolean allowaccessmodification : new Boolean[] {false, true}) {
- configs.add(new Object[] {"Inlining", backend, minification, allowaccessmodification});
- }
- }
- }
- return configs;
+ return buildParameters(
+ ImmutableList.of("Inlining"),
+ Backend.values(),
+ BooleanUtils.values(),
+ BooleanUtils.values());
}
private final String name;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
index 49a9457..eedfc2f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -69,9 +69,11 @@
// Check that the goto in block0 remains. There was a bug in the trivial goto elimination
// that ended up removing that goto changing the code to start with the unreachable
// throw.
+ InternalOptions options = new InternalOptions();
+ options.debug = true;
IRCode code =
new IRCode(
- new InternalOptions(),
+ options,
null,
blocks,
new ValueNumberGenerator(),
@@ -150,9 +152,11 @@
// Check that the goto in block0 remains. There was a bug in the trivial goto elimination
// that ended up removing that goto changing the code to start with the unreachable
// throw.
+ InternalOptions options = new InternalOptions();
+ options.debug = true;
IRCode code =
new IRCode(
- new InternalOptions(),
+ options,
null,
blocks,
new ValueNumberGenerator(),
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java
index e8ed51f..ef7a07b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import java.util.Collection;
import org.junit.runner.RunWith;
@@ -22,12 +22,7 @@
@Parameterized.Parameters(name = "Backend: {0} minification: {1}")
public static Collection<Object[]> data() {
- ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
- for (Backend backend : Backend.values()) {
- builder.add(new Object[]{backend, Boolean.TRUE});
- builder.add(new Object[]{backend, Boolean.FALSE});
- }
- return builder.build();
+ return buildParameters(Backend.values(), BooleanUtils.values());
}
GetNameTestBase(Backend backend, boolean enableMinification) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index c184e6e..0794d92 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -9,9 +9,14 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.VmTestRunner;
@@ -36,13 +41,16 @@
import com.android.tools.r8.ir.optimize.staticizer.trivial.Simple;
import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithGetter;
import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithParams;
+import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithPhi;
import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithSideEffects;
import com.android.tools.r8.ir.optimize.staticizer.trivial.TrivialTestClass;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import java.nio.file.Path;
@@ -50,26 +58,49 @@
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
-@RunWith(VmTestRunner.class)
+@RunWith(Parameterized.class)
public class ClassStaticizerTest extends TestBase {
+ private Backend backend;
+
+ @Parameterized.Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ public ClassStaticizerTest(Backend backend) {
+ this.backend = backend;
+ }
+
@Test
public void testTrivial() throws Exception {
- byte[][] classes = {
- ToolHelper.getClassAsBytes(TrivialTestClass.class),
- ToolHelper.getClassAsBytes(Simple.class),
- ToolHelper.getClassAsBytes(SimpleWithSideEffects.class),
- ToolHelper.getClassAsBytes(SimpleWithParams.class),
- ToolHelper.getClassAsBytes(SimpleWithGetter.class),
+ assumeTrue("b/112831361", backend == Backend.DEX);
+ Class<?> main = TrivialTestClass.class;
+ Class<?>[] classes = {
+ NeverInline.class,
+ TrivialTestClass.class,
+ Simple.class,
+ SimpleWithSideEffects.class,
+ SimpleWithParams.class,
+ SimpleWithGetter.class,
+ SimpleWithPhi.class,
+ SimpleWithPhi.Companion.class
};
- AndroidApp app = runR8(buildAndroidApp(classes), TrivialTestClass.class);
+ String javaOutput = runOnJava(main);
+ TestRunResult result = testForR8(backend)
+ .addProgramClasses(classes)
+ .enableProguardTestOptions()
+ .enableInliningAnnotations()
+ .addKeepMainRule(main)
+ .addKeepRules("-dontobfuscate", "-allowaccessmodification")
+ .addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
+ .addOptionsModification(this::configure)
+ .run(main)
+ .assertSuccessWithOutput(javaOutput);
- String javaOutput = runOnJava(TrivialTestClass.class);
- String artOutput = runOnArt(app, TrivialTestClass.class);
- assertEquals(javaOutput, artOutput);
-
- CodeInspector inspector = new CodeInspector(app);
- ClassSubject clazz = inspector.clazz(TrivialTestClass.class);
+ CodeInspector inspector = result.inspector();
+ ClassSubject clazz = inspector.clazz(main);
assertEquals(
Lists.newArrayList(
@@ -82,6 +113,16 @@
assertEquals(
Lists.newArrayList(
+ "STATIC: String trivial.SimpleWithPhi.bar(String)",
+ "STATIC: String trivial.SimpleWithPhi.foo()",
+ "STATIC: String trivial.SimpleWithPhi.foo()",
+ "STATIC: String trivial.TrivialTestClass.next()"),
+ references(clazz, "testSimpleWithPhi", "void", "int"));
+
+ assertTrue(instanceMethods(inspector.clazz(SimpleWithPhi.class)).isEmpty());
+
+ assertEquals(
+ Lists.newArrayList(
"STATIC: String trivial.SimpleWithParams.bar(String)",
"STATIC: String trivial.SimpleWithParams.foo()",
"STATIC: String trivial.TrivialTestClass.next()"),
@@ -115,25 +156,33 @@
@Test
public void testMoveToHost() throws Exception {
- byte[][] classes = {
- ToolHelper.getClassAsBytes(MoveToHostTestClass.class),
- ToolHelper.getClassAsBytes(HostOk.class),
- ToolHelper.getClassAsBytes(CandidateOk.class),
- ToolHelper.getClassAsBytes(HostOkSideEffects.class),
- ToolHelper.getClassAsBytes(CandidateOkSideEffects.class),
- ToolHelper.getClassAsBytes(HostConflictMethod.class),
- ToolHelper.getClassAsBytes(CandidateConflictMethod.class),
- ToolHelper.getClassAsBytes(HostConflictField.class),
- ToolHelper.getClassAsBytes(CandidateConflictField.class),
+ assumeTrue("b/112831361", backend == Backend.DEX);
+ Class<?> main = MoveToHostTestClass.class;
+ Class<?>[] classes = {
+ NeverInline.class,
+ MoveToHostTestClass.class,
+ HostOk.class,
+ CandidateOk.class,
+ HostOkSideEffects.class,
+ CandidateOkSideEffects.class,
+ HostConflictMethod.class,
+ CandidateConflictMethod.class,
+ HostConflictField.class,
+ CandidateConflictField.class
};
- AndroidApp app = runR8(buildAndroidApp(classes), MoveToHostTestClass.class);
+ String javaOutput = runOnJava(main);
+ TestRunResult result = testForR8(backend)
+ .addProgramClasses(classes)
+ .enableProguardTestOptions()
+ .enableInliningAnnotations()
+ .addKeepMainRule(main)
+ .addKeepRules("-dontobfuscate", "-allowaccessmodification")
+ .addOptionsModification(this::configure)
+ .run(main)
+ .assertSuccessWithOutput(javaOutput);
- String javaOutput = runOnJava(MoveToHostTestClass.class);
- String artOutput = runOnArt(app, MoveToHostTestClass.class);
- assertEquals(javaOutput, artOutput);
-
- CodeInspector inspector = new CodeInspector(app);
- ClassSubject clazz = inspector.clazz(MoveToHostTestClass.class);
+ CodeInspector inspector = result.inspector();
+ ClassSubject clazz = inspector.clazz(main);
assertEquals(
Lists.newArrayList(
@@ -232,40 +281,6 @@
return type.toSourceString().startsWith("com.android.tools.r8.ir.optimize.staticizer");
}
- private AndroidApp runR8(AndroidApp app, Class mainClass) throws Exception {
- AndroidApp compiled =
- compileWithR8(app, getProguardConfig(mainClass.getCanonicalName()), this::configure);
-
- // Materialize file for execution.
- Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
- compiled.writeToZip(generatedDexFile, OutputMode.DexIndexed);
-
- // Run with ART.
- String artOutput = ToolHelper.runArtNoVerificationErrors(
- generatedDexFile.toString(), mainClass.getCanonicalName());
-
- // Compare with Java.
- ProcessResult javaResult = ToolHelper.runJava(
- ToolHelper.getClassPathForTests(), mainClass.getCanonicalName());
-
- if (javaResult.exitCode != 0) {
- System.out.println(javaResult.stdout);
- System.err.println(javaResult.stderr);
- fail("JVM failed for: " + mainClass);
- }
- assertEquals("JVM and ART output differ", javaResult.stdout, artOutput);
-
- return compiled;
- }
-
- private String getProguardConfig(String main) {
- return keepMainProguardConfiguration(main)
- + System.lineSeparator()
- + "-dontobfuscate"
- + System.lineSeparator()
- + "-allowaccessmodification";
- }
-
private void configure(InternalOptions options) {
options.enableClassInlining = false;
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictField.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictField.java
index f2108b0..82eef8e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictField.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictField.java
@@ -4,18 +4,18 @@
package com.android.tools.r8.ir.optimize.staticizer.movetohost;
+import com.android.tools.r8.NeverInline;
+
public class CandidateConflictField {
public static String field;
+ @NeverInline
public String foo() {
- synchronized ("") {
- return bar("CandidateConflictMethod::foo()");
- }
+ return bar("CandidateConflictMethod::foo()");
}
+ @NeverInline
public String bar(String other) {
- synchronized ("") {
- return "CandidateConflictMethod::bar(" + other + ")";
- }
+ return "CandidateConflictMethod::bar(" + other + ")";
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictMethod.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictMethod.java
index 40d0576..4e5ce3a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictMethod.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictMethod.java
@@ -4,16 +4,16 @@
package com.android.tools.r8.ir.optimize.staticizer.movetohost;
+import com.android.tools.r8.NeverInline;
+
public class CandidateConflictMethod {
+ @NeverInline
public String foo() {
- synchronized ("") {
- return bar("CandidateConflictMethod::foo()");
- }
+ return bar("CandidateConflictMethod::foo()");
}
+ @NeverInline
public String bar(String other) {
- synchronized ("") {
- return "CandidateConflictMethod::bar(" + other + ")";
- }
+ return "CandidateConflictMethod::bar(" + other + ")";
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOk.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOk.java
index daf5647..0834c000 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOk.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOk.java
@@ -4,22 +4,21 @@
package com.android.tools.r8.ir.optimize.staticizer.movetohost;
+import com.android.tools.r8.NeverInline;
+
public class CandidateOk {
+ @NeverInline
public String foo() {
- synchronized ("") {
- return bar("CandidateOk::foo()");
- }
+ return bar("CandidateOk::foo()");
}
+ @NeverInline
public String bar(String other) {
- synchronized ("") {
- return "CandidateOk::bar(" + other + ")";
- }
+ return "CandidateOk::bar(" + other + ")";
}
+ @NeverInline
public void blah(String other) {
- synchronized ("") {
- System.out.println("CandidateOk::blah(" + other + ")");
- }
+ System.out.println("CandidateOk::blah(" + other + ")");
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOkSideEffects.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOkSideEffects.java
index 2077056..a02bed3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOkSideEffects.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOkSideEffects.java
@@ -4,16 +4,16 @@
package com.android.tools.r8.ir.optimize.staticizer.movetohost;
+import com.android.tools.r8.NeverInline;
+
public class CandidateOkSideEffects {
+ @NeverInline
public String foo() {
- synchronized ("") {
- return bar("CandidateOkSideEffects::foo()");
- }
+ return bar("CandidateOkSideEffects::foo()");
}
+ @NeverInline
public String bar(String other) {
- synchronized ("") {
- return "CandidateOkSideEffects::bar(" + other + ")";
- }
+ return "CandidateOkSideEffects::bar(" + other + ")";
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/HostConflictMethod.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/HostConflictMethod.java
index ad86b35..8804e14 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/HostConflictMethod.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/HostConflictMethod.java
@@ -4,12 +4,13 @@
package com.android.tools.r8.ir.optimize.staticizer.movetohost;
+import com.android.tools.r8.NeverInline;
+
public class HostConflictMethod {
static CandidateConflictMethod INSTANCE = new CandidateConflictMethod();
+ @NeverInline
public String bar(String other) {
- synchronized ("") {
- return "HostConflictMethod::bar(" + other + ")";
- }
+ return "HostConflictMethod::bar(" + other + ")";
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/MoveToHostTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/MoveToHostTestClass.java
index 3f31839..3be6e5f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/MoveToHostTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/MoveToHostTestClass.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.staticizer.movetohost;
+import com.android.tools.r8.NeverInline;
+
public class MoveToHostTestClass {
private static int ID = 0;
@@ -19,24 +21,28 @@
test.testConflictField();
}
- private synchronized void testOk() {
+ @NeverInline
+ private void testOk() {
System.out.println(HostOk.INSTANCE.foo());
System.out.println(HostOk.INSTANCE.bar(next()));
HostOk.INSTANCE.blah(next());
}
- private synchronized void testOkSideEffects() {
+ @NeverInline
+ private void testOkSideEffects() {
System.out.println(HostOkSideEffects.INSTANCE.foo());
System.out.println(HostOkSideEffects.INSTANCE.bar(next()));
}
- private synchronized void testConflictMethod() {
+ @NeverInline
+ private void testConflictMethod() {
System.out.println(new HostConflictMethod().bar(next()));
System.out.println(HostConflictMethod.INSTANCE.foo());
System.out.println(HostConflictMethod.INSTANCE.bar(next()));
}
- private synchronized void testConflictField() {
+ @NeverInline
+ private void testConflictField() {
System.out.println(new HostConflictField().field);
System.out.println(CandidateConflictField.field);
System.out.println(HostConflictField.INSTANCE.foo());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/Simple.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/Simple.java
index 8df079c..3cb1c49 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/Simple.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/Simple.java
@@ -4,18 +4,18 @@
package com.android.tools.r8.ir.optimize.staticizer.trivial;
+import com.android.tools.r8.NeverInline;
+
public class Simple {
static Simple INSTANCE = new Simple();
+ @NeverInline
String foo() {
- synchronized ("") {
- return bar("Simple::foo()");
- }
+ return bar("Simple::foo()");
}
+ @NeverInline
String bar(String other) {
- synchronized ("") {
- return "Simple::bar(" + other + ")";
- }
+ return "Simple::bar(" + other + ")";
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithGetter.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithGetter.java
index 9ff20c3..55487ac 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithGetter.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithGetter.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.staticizer.trivial;
+import com.android.tools.r8.NeverInline;
+
public class SimpleWithGetter {
private static SimpleWithGetter INSTANCE = new SimpleWithGetter();
@@ -11,15 +13,13 @@
return INSTANCE;
}
+ @NeverInline
String foo() {
- synchronized ("") {
- return bar("Simple::foo()");
- }
+ return bar("Simple::foo()");
}
+ @NeverInline
String bar(String other) {
- synchronized ("") {
- return "Simple::bar(" + other + ")";
- }
+ return "Simple::bar(" + other + ")";
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithParams.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithParams.java
index a71f1f5..24edea4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithParams.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithParams.java
@@ -4,21 +4,21 @@
package com.android.tools.r8.ir.optimize.staticizer.trivial;
+import com.android.tools.r8.NeverInline;
+
public class SimpleWithParams {
static SimpleWithParams INSTANCE = new SimpleWithParams(123);
SimpleWithParams(int i) {
}
+ @NeverInline
String foo() {
- synchronized ("") {
- return bar("SimpleWithParams::foo()");
- }
+ return bar("SimpleWithParams::foo()");
}
+ @NeverInline
String bar(String other) {
- synchronized ("") {
- return "SimpleWithParams::bar(" + other + ")";
- }
+ return "SimpleWithParams::bar(" + other + ")";
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithPhi.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithPhi.java
new file mode 100644
index 0000000..f3c76cb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithPhi.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.staticizer.trivial;
+
+import com.android.tools.r8.NeverInline;
+
+public class SimpleWithPhi {
+ public static class Companion {
+ @NeverInline
+ String foo() {
+ return bar("SimpleWithPhi$Companion::foo()");
+ }
+
+ @NeverInline
+ String bar(String other) {
+ return "SimpleWithPhi$Companion::bar(" + other + ")";
+ }
+ }
+
+ static Companion INSTANCE = new Companion();
+
+ @NeverInline
+ static String foo() {
+ return INSTANCE.foo();
+ }
+
+ @NeverInline
+ static String bar(String other) {
+ if (other.length() > 2) {
+ return INSTANCE.foo();
+ } else {
+ return INSTANCE.bar(other);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithSideEffects.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithSideEffects.java
index 5ba97dc..2ee78f5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithSideEffects.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithSideEffects.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.staticizer.trivial;
+import com.android.tools.r8.NeverInline;
+
public class SimpleWithSideEffects {
static SimpleWithSideEffects INSTANCE = new SimpleWithSideEffects();
@@ -11,15 +13,13 @@
System.out.println("SimpleWithSideEffects::<clinit>()");
}
+ @NeverInline
String foo() {
- synchronized ("") {
- return bar("SimpleWithSideEffects::foo()");
- }
+ return bar("SimpleWithSideEffects::foo()");
}
+ @NeverInline
String bar(String other) {
- synchronized ("") {
- return "SimpleWithSideEffects::bar(" + other + ")";
- }
+ return "SimpleWithSideEffects::bar(" + other + ")";
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/TrivialTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/TrivialTestClass.java
index 2dad273..964567c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/TrivialTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/TrivialTestClass.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.staticizer.trivial;
+import com.android.tools.r8.NeverInline;
+
public class TrivialTestClass {
private static int ID = 0;
@@ -14,27 +16,47 @@
public static void main(String[] args) {
TrivialTestClass test = new TrivialTestClass();
test.testSimple();
+ test.testSimpleWithPhi(args.length);
test.testSimpleWithSideEffects();
test.testSimpleWithParams();
test.testSimpleWithGetter();
}
- private synchronized void testSimple() {
+ @NeverInline
+ private void testSimple() {
System.out.println(Simple.INSTANCE.foo());
System.out.println(Simple.INSTANCE.bar(next()));
}
- private synchronized void testSimpleWithSideEffects() {
+ @NeverInline
+ private void testSimpleWithPhi(int arg) {
+ switch (arg) {
+ case 0:
+ System.out.println(SimpleWithPhi.foo() + " " + true);
+ break;
+ case 2:
+ System.out.println(SimpleWithPhi.foo() + " " + false);
+ break;
+ default:
+ System.out.println(SimpleWithPhi.bar(next()));
+ break;
+ }
+ }
+
+ @NeverInline
+ private void testSimpleWithSideEffects() {
System.out.println(SimpleWithSideEffects.INSTANCE.foo());
System.out.println(SimpleWithSideEffects.INSTANCE.bar(next()));
}
- private synchronized void testSimpleWithParams() {
+ @NeverInline
+ private void testSimpleWithParams() {
System.out.println(SimpleWithParams.INSTANCE.foo());
System.out.println(SimpleWithParams.INSTANCE.bar(next()));
}
- private synchronized void testSimpleWithGetter() {
+ @NeverInline
+ private void testSimpleWithGetter() {
System.out.println(SimpleWithGetter.getInstance().foo());
System.out.println(SimpleWithGetter.getInstance().bar(next()));
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java
new file mode 100644
index 0000000..d9dfcd8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java
@@ -0,0 +1,136 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.uninstantiatedtypes;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Ignore;
+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 ParameterRewritingTest extends TestBase {
+
+ private final Backend backend;
+
+ @Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ public ParameterRewritingTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Ignore("b/110806787")
+ @Test
+ public void test() throws Exception {
+ String expected =
+ StringUtils.lines(
+ "Factory.createStatic() -> null",
+ "Factory.createStaticWithUnused1() -> null",
+ "Factory.createStaticWithUnused2() -> null",
+ "Factory.createStaticWithUnused3() -> null");
+
+ testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expected);
+
+ CodeInspector inspector =
+ testForR8(backend)
+ .addInnerClasses(ParameterRewritingTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .addKeepRules("-dontobfuscate")
+ .addOptionsModification(options -> options.enableClassInlining = false)
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expected)
+ .inspector();
+
+ ClassSubject factoryClassSubject = inspector.clazz(Factory.class);
+ MethodSubject createStaticMethodSubject =
+ factoryClassSubject.uniqueMethodWithName("createStatic");
+ assertThat(createStaticMethodSubject, isPresent());
+ assertEquals(1, createStaticMethodSubject.getMethod().method.proto.parameters.size());
+
+ for (int i = 1; i <= 3; ++i) {
+ String createStaticWithUnusedMethodName = "createStaticWithUnused" + i;
+ MethodSubject createStaticWithUnusedMethodSubject =
+ factoryClassSubject.uniqueMethodWithName(createStaticWithUnusedMethodName);
+ assertThat(createStaticWithUnusedMethodSubject, isPresent());
+
+ DexMethod method = createStaticWithUnusedMethodSubject.getMethod().method;
+ assertEquals(1, method.proto.parameters.size());
+ assertEquals("java.lang.String", method.proto.parameters.toString());
+ }
+
+ assertThat(inspector.clazz(Uninstantiated.class), not(isPresent()));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Object obj1 = Factory.createStatic(null, "Factory.createStatic()");
+ System.out.println(" -> " + obj1);
+
+ Object obj2 =
+ Factory.createStaticWithUnused1(new Object(), null, "Factory.createStaticWithUnused1()");
+ System.out.println(" -> " + obj2);
+
+ Object obj3 =
+ Factory.createStaticWithUnused2(null, new Object(), "Factory.createStaticWithUnused2()");
+ System.out.println(" -> " + obj3);
+
+ Object obj4 =
+ Factory.createStaticWithUnused3(null, "Factory.createStaticWithUnused3()", new Object());
+ System.out.println(" -> " + obj4);
+ }
+ }
+
+ @NeverMerge
+ static class Uninstantiated {}
+
+ @NeverMerge
+ static class Factory {
+
+ @NeverInline
+ public static Object createStatic(Uninstantiated uninstantiated, String msg) {
+ System.out.print(msg);
+ return uninstantiated;
+ }
+
+ @NeverInline
+ public static Object createStaticWithUnused1(
+ Object unused, Uninstantiated uninstantiated, String msg) {
+ System.out.print(msg);
+ return uninstantiated;
+ }
+
+ @NeverInline
+ public static Object createStaticWithUnused2(
+ Uninstantiated uninstantiated, Object unused, String msg) {
+ System.out.print(msg);
+ return uninstantiated;
+ }
+
+ @NeverInline
+ public static Object createStaticWithUnused3(
+ Uninstantiated uninstantiated, String msg, Object unused) {
+ System.out.print(msg);
+ return uninstantiated;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
new file mode 100644
index 0000000..0fb68e6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
@@ -0,0 +1,149 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.uninstantiatedtypes;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Ignore;
+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 VoidReturnTypeRewritingTest extends TestBase {
+
+ private final Backend backend;
+
+ @Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ public VoidReturnTypeRewritingTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Ignore("b/110806787")
+ @Test
+ public void test() throws Exception {
+ String expected =
+ StringUtils.lines(
+ "Factory.createStatic() -> null",
+ "Factory.createVirtual() -> null",
+ "SubFactory.createVirtual() -> null",
+ "SubSubFactory.createVirtual() -> null");
+
+ testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expected);
+
+ CodeInspector inspector =
+ testForR8(backend)
+ .addInnerClasses(VoidReturnTypeRewritingTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .addKeepRules("-dontobfuscate")
+ .addOptionsModification(options -> options.enableClassInlining = false)
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expected)
+ .inspector();
+
+ ClassSubject factoryClassSubject = inspector.clazz(Factory.class);
+ MethodSubject createStaticMethodSubject =
+ factoryClassSubject.uniqueMethodWithName("createStatic");
+ assertThat(createStaticMethodSubject, isPresent());
+ assertTrue(createStaticMethodSubject.getMethod().method.proto.returnType.isVoidType());
+ MethodSubject createVirtualMethodSubject =
+ factoryClassSubject.uniqueMethodWithName("createVirtual");
+ assertThat(createVirtualMethodSubject, isPresent());
+ assertTrue(createVirtualMethodSubject.getMethod().method.proto.returnType.isVoidType());
+
+ createVirtualMethodSubject =
+ inspector.clazz(SubFactory.class).uniqueMethodWithName("createVirtual");
+ assertThat(createVirtualMethodSubject, isPresent());
+ assertTrue(createVirtualMethodSubject.getMethod().method.proto.returnType.isVoidType());
+
+ ClassSubject subSubFactoryClassSubject = inspector.clazz(SubSubFactory.class);
+ assertThat(subSubFactoryClassSubject.method("void", "createVirtual"), isPresent());
+ assertThat(
+ subSubFactoryClassSubject.method(SubUninstantiated.class.getTypeName(), "createVirtual"),
+ isPresent());
+
+ // TODO(b/110806787): Uninstantiated is kept because SubUninstantiated inherits from it.
+ // We should consider rewriting SubUninstantiated such that it no longer inherits from
+ // Uninstantiated.
+ assertThat(inspector.clazz(Uninstantiated.class), isPresent());
+ assertThat(inspector.clazz(SubUninstantiated.class), isPresent());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Uninstantiated obj1 = Factory.createStatic();
+ System.out.println(" -> " + obj1);
+
+ Uninstantiated obj2 = new Factory().createVirtual();
+ System.out.println(" -> " + obj2);
+
+ Uninstantiated obj3 = new SubFactory().createVirtual();
+ System.out.println(" -> " + obj3);
+
+ Uninstantiated obj4 = new SubSubFactory().createVirtual();
+ System.out.println(" -> " + obj4);
+ }
+ }
+
+ @NeverMerge
+ static class Uninstantiated {}
+
+ @NeverMerge
+ static class SubUninstantiated extends Uninstantiated {}
+
+ @NeverMerge
+ static class Factory {
+
+ @NeverInline
+ public static Uninstantiated createStatic() {
+ System.out.print("Factory.createStatic()");
+ return null;
+ }
+
+ @NeverInline
+ public Uninstantiated createVirtual() {
+ System.out.print("Factory.createVirtual()");
+ return null;
+ }
+ }
+
+ @NeverMerge
+ static class SubFactory extends Factory {
+
+ @Override
+ @NeverInline
+ public Uninstantiated createVirtual() {
+ System.out.print("SubFactory.createVirtual()");
+ return null;
+ }
+ }
+
+ @NeverMerge
+ static class SubSubFactory extends SubFactory {
+
+ @Override
+ @NeverInline
+ public SubUninstantiated createVirtual() {
+ System.out.print("SubSubFactory.createVirtual()");
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
index 7e4b76a..5ed3cc5 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -21,7 +21,7 @@
private static class MockRegisterAllocator implements RegisterAllocator {
@Override
- public void allocateRegisters(boolean debug) {
+ public void allocateRegisters() {
}
@Override
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index b76e947..9380115 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -59,12 +60,7 @@
@Parameters(name = "allowAccessModification: {0} target: {1}")
public static Collection<Object[]> data() {
- ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
- for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
- builder.add(new Object[]{Boolean.TRUE, targetVersion});
- builder.add(new Object[]{Boolean.FALSE, targetVersion});
- }
- return builder.build();
+ return buildParameters(BooleanUtils.values(), KotlinTargetVersion.values());
}
protected void addExtraClasspath(Path path) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 7d73d8f..1d3f25e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -8,8 +8,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.code.NewInstance;
import com.android.tools.r8.code.SgetObject;
import com.android.tools.r8.graph.DexClass;
@@ -20,28 +20,17 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
-import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
-import org.junit.runners.Parameterized.Parameters;
public class KotlinClassInlinerTest extends AbstractR8KotlinTestBase {
- @Parameters(name = "allowAccessModification: {0} target: {1}")
- public static Collection<Object[]> data() {
- ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
- for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
- builder.add(new Object[]{Boolean.TRUE, targetVersion});
- }
- return builder.build();
- }
private static boolean isLambda(DexClass clazz) {
return !clazz.type.getPackageDescriptor().startsWith("kotlin") &&
@@ -67,6 +56,7 @@
@Test
public void testJStyleLambdas() throws Exception {
+ assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_inliner_lambda_j_style.MainKt";
runTest("class_inliner_lambda_j_style", mainClassName, false, (app) -> {
CodeInspector inspector = new CodeInspector(app);
@@ -112,6 +102,7 @@
@Test
public void testKStyleLambdas() throws Exception {
+ assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_inliner_lambda_k_style.MainKt";
runTest("class_inliner_lambda_k_style", mainClassName, false, (app) -> {
CodeInspector inspector = new CodeInspector(app);
@@ -186,6 +177,7 @@
@Test
public void testDataClass() throws Exception {
+ assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_inliner_data_class.MainKt";
runTest("class_inliner_data_class", mainClassName, true, (app) -> {
CodeInspector inspector = new CodeInspector(app);
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index c19b80e..ced08f5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -7,28 +7,18 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.ImmutableList;
-import java.util.Collection;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
-import org.junit.runners.Parameterized.Parameters;
public class KotlinClassStaticizerTest extends AbstractR8KotlinTestBase {
- @Parameters(name = "allowAccessModification: {0} target: {1}")
- public static Collection<Object[]> data() {
- ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
- for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
- builder.add(new Object[]{Boolean.TRUE, targetVersion});
- }
- return builder.build();
- }
@Test
public void testCompanionAndRegularObjects() throws Exception {
+ assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_staticizer.MainKt";
// Without class staticizer.
diff --git a/src/test/java/com/android/tools/r8/kotlin/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/MetadataStripTest.java
index 2abc1ed..1ab3147 100644
--- a/src/test/java/com/android/tools/r8/kotlin/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/MetadataStripTest.java
@@ -47,13 +47,7 @@
@Parameterized.Parameters(name = "Backend: {0} target: {1}")
public static Collection<Object[]> data() {
- ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
- for (Backend backend : Backend.values()) {
- for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
- builder.add(new Object[]{backend, targetVersion});
- }
- }
- return builder.build();
+ return buildParameters(Backend.values(), KotlinTargetVersion.values());
}
public MetadataStripTest(Backend backend, KotlinTargetVersion targetVersion) {
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
index 4a7db37..0f3accb 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
@@ -168,7 +168,8 @@
getProguardConfig(enableAdaptResourceFileContents, adaptResourceFileContentsPathFilter),
"-neverinline class com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B {",
" public void method();",
- "}");
+ "}",
+ "-neverclassinline class com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B");
}
@Test
@@ -324,13 +325,7 @@
.addDataResources(getDataResources())
.enableProguardTestOptions()
.addKeepRules(proguardConfig)
- .addOptionsModification(
- o -> {
- // TODO(christofferqa): Class inliner should respect -neverinline.
- o.enableClassInlining = false;
- o.enableVerticalClassMerging = true;
- o.dataResourceConsumer = dataResourceConsumer;
- })
+ .addOptionsModification(o -> o.dataResourceConsumer = dataResourceConsumer)
.compile();
}
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index ecfddf4..095d7e9 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.utils.ArchiveResourceProvider;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.KeepingDiagnosticHandler;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.io.File;
@@ -93,15 +94,15 @@
private static String getProguardConfigWithNeverInline(
boolean enableAdaptResourceFileNames, String adaptResourceFileNamesPathFilter) {
- return String.join(
- System.lineSeparator(),
+ return StringUtils.lines(
getProguardConfig(enableAdaptResourceFileNames, adaptResourceFileNamesPathFilter),
"-neverinline class " + adaptresourcefilenames.A.class.getName() + " {",
" public void method();",
"}",
"-neverinline class " + adaptresourcefilenames.B.Inner.class.getName() + " {",
" public void method();",
- "}");
+ "}",
+ "-neverclassinline class *");
}
@Test
@@ -262,9 +263,6 @@
return ToolHelper.runR8(
command,
options -> {
- // TODO(christofferqa): Class inliner should respect -neverinline.
- options.enableClassInlining = false;
- options.enableVerticalClassMerging = true;
options.dataResourceConsumer = dataResourceConsumer;
options.proguardMapConsumer = proguardMapConsumer;
options.testing.suppressExperimentalCfBackendWarning = true;
diff --git a/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
index 5e9b9c8..0d2ab3a 100644
--- a/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
+++ b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
@@ -9,9 +9,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -22,13 +19,9 @@
private final Backend backend;
- @Parameters(name = "mode:{0}")
- public static Collection<Object[]> data() {
- List<Object[]> parameters = new ArrayList<>();
- for (Backend backend : Backend.values()) {
- parameters.add(new Object[] {backend});
- }
- return parameters;
+ @Parameters(name = "backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
}
public B114554345(Backend backend) {
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
index e1c61c1..b110ead 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
@@ -22,7 +22,6 @@
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -39,13 +38,7 @@
@Parameters(name = "Backend: {0}, mode: {1}")
public static Collection<Object[]> data() {
- List<Object[]> parameters = new ArrayList<>();
- for (Backend backend : Backend.values()) {
- for (CompilationMode mode : CompilationMode.values()) {
- parameters.add(new Object[] {backend, mode});
- }
- }
- return parameters;
+ return buildParameters(Backend.values(), CompilationMode.values());
}
public RetraceTest(Backend backend, CompilationMode mode) {
diff --git a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
new file mode 100644
index 0000000..edd6fd6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
@@ -0,0 +1,181 @@
+// Copyright (c) 2018 the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.reachabilitysensitive;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.code.AddIntLit8;
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import dalvik.annotation.optimization.ReachabilitySensitive;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+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.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+class TestClass {
+ public void method() {
+ int i = 2;
+ int j = i + 1;
+ int k = j + 2;
+ System.out.println(k);
+ }
+}
+
+class TestClassWithAnnotatedField {
+ @ReachabilitySensitive private final long field = 0;
+
+ public void method() {
+ int i = 2;
+ int j = i + 1;
+ int k = j + 2;
+ System.out.println(k);
+ }
+}
+
+class TestClassWithAnnotatedMethod {
+
+ @ReachabilitySensitive
+ public void unrelatedAnnotatedMethod() {}
+
+ public void method() {
+ int i = 2;
+ int j = i + 1;
+ int k = j + 2;
+ System.out.println(k);
+ }
+}
+
+@RunWith(Parameterized.class)
+public class ReachabilitySensitiveTest extends TestBase {
+
+ private final Tool tool;
+
+ @Parameters(name = "{0}")
+ public static List<Object> data() {
+ return ImmutableList.of(Tool.D8, Tool. R8);
+ }
+
+ public ReachabilitySensitiveTest(Tool tool) {
+ this.tool = tool;
+ }
+
+ @Test
+ public void testNoAnnotation()
+ throws IOException, CompilationFailedException, ExecutionException, NoSuchMethodException {
+ CodeInspector inspector = tool == Tool.R8
+ ? compileR8(TestClass.class)
+ : compile(TestClass.class);
+ DexCode code =
+ inspector.method(TestClass.class.getMethod("method")).getMethod().getCode().asDexCode();
+ // Computation of k is constant folded and the value takes up one register. System.out takes
+ // up another register and the receiver is the last.
+ assertEquals(3, code.registerSize);
+ checkNoLocals(code);
+ }
+
+ @Test
+ public void testFieldAnnotation()
+ throws IOException, CompilationFailedException, ExecutionException, NoSuchMethodException {
+ CodeInspector inspector = tool == Tool.R8
+ ? compileR8(TestClassWithAnnotatedField.class)
+ : compile(TestClassWithAnnotatedField.class);
+ checkAnnotatedCode(
+ inspector
+ .method(TestClassWithAnnotatedField.class.getMethod("method"))
+ .getMethod()
+ .getCode()
+ .asDexCode());
+ }
+
+ @Test
+ public void testMethodAnnotation()
+ throws IOException, CompilationFailedException, ExecutionException, NoSuchMethodException {
+ CodeInspector inspector = tool == Tool.R8
+ ? compileR8(TestClassWithAnnotatedMethod.class)
+ : compile(TestClassWithAnnotatedMethod.class);
+ checkAnnotatedCode(
+ inspector
+ .method(TestClassWithAnnotatedMethod.class.getMethod("method"))
+ .getMethod()
+ .getCode()
+ .asDexCode());
+ }
+
+ private void checkNoLocals(DexCode code) {
+ // Even if we preserve live range of locals, we do not output locals information
+ // as this is a release build.
+ assertTrue(Arrays.stream(code.getDebugInfo().events)
+ .allMatch(event -> !(event instanceof StartLocal)));
+ }
+
+ private void checkAnnotatedCode(DexCode code) {
+ // All live at the same time: receiver, i, j, k, System.out.
+ assertEquals(5, code.registerSize);
+ Instruction first = code.instructions[0];
+ Instruction second = code.instructions[1];
+ Instruction third = code.instructions[2];
+ // None of the local declarations overwrite other locals.
+ assertTrue(first instanceof Const4);
+ assertTrue(second instanceof AddIntLit8);
+ assertTrue(third instanceof AddIntLit8);
+ int firstRegister = ((Const4) first).A;
+ int secondRegister = ((AddIntLit8) second).AA;
+ int thirdRegister = ((AddIntLit8) third).AA;
+ assertFalse(firstRegister == secondRegister);
+ assertFalse(firstRegister == thirdRegister);
+ assertFalse(secondRegister == thirdRegister);
+ checkNoLocals(code);
+ }
+
+ private CodeInspector compile(Class... classes)
+ throws CompilationFailedException, IOException, ExecutionException {
+ return testForD8()
+ .addProgramClasses(classes)
+ .setMode(CompilationMode.RELEASE)
+ .compile()
+ .inspector();
+ }
+
+ private CodeInspector compileR8(Class... classes)
+ throws CompilationFailedException, IOException, ExecutionException {
+ List<String> keepRules =
+ Arrays.stream(classes)
+ .map(c -> "-keep class " + c.getCanonicalName() + " { <methods>; }")
+ .collect(Collectors.toList());
+ return testForR8(Backend.DEX)
+ .addProgramClasses(classes)
+ // TODO(ager): This will be in android.jar over time. For now, make it part of the app.
+ .addProgramClasses(ReachabilitySensitive.class)
+ .setMode(CompilationMode.RELEASE)
+ // Keep the input class and its methods.
+ .addKeepRules(keepRules)
+ // Keep the annotation class.
+ .addKeepRules("-keep class dalvik.annotation.optimization.ReachabilitySensitive")
+ // Keep the annotation and debug information which is needed for debug mode to actually
+ // keep things alive.
+ .addKeepRules("-keepattributes *Annotations*,LineNumberTable," +
+ "LocalVariableTable,LocalVariableTypeTable")
+ .compile()
+ .inspector();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java b/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
index daf0bc2..ba79ec5 100644
--- a/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
+++ b/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
@@ -21,9 +21,9 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -50,13 +50,7 @@
@Parameterized.Parameters(name = "Backend: {0}, Minify: {1}")
public static Collection<Object[]> data() {
- List<Object[]> result = new ArrayList<>();
- for (Backend backend : Backend.values()) {
- for (boolean minify : new boolean[] {false, true}) {
- result.add(new Object[] {backend, minify});
- }
- }
- return result;
+ return buildParameters(Backend.values(), BooleanUtils.values());
}
public KeepDirectoriesTest(Backend backend, boolean minify) {
diff --git a/src/test/java/com/android/tools/r8/shaking/AtomicFieldUpdaterTest.java b/src/test/java/com/android/tools/r8/shaking/AtomicFieldUpdaterTest.java
index 7e262ee..8dcdd38 100644
--- a/src/test/java/com/android/tools/r8/shaking/AtomicFieldUpdaterTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/AtomicFieldUpdaterTest.java
@@ -15,9 +15,6 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.File;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,13 +26,9 @@
private final Backend backend;
- @Parameters(name = "mode:{0}")
- public static Collection<Object[]> data() {
- List<Object[]> parameters = new ArrayList<>();
- for (Backend backend : Backend.values()) {
- parameters.add(new Object[] {backend});
- }
- return parameters;
+ @Parameters(name = "backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
}
public AtomicFieldUpdaterTest(Backend backend) {
diff --git a/src/test/java/com/android/tools/r8/shaking/ClassKindTest.java b/src/test/java/com/android/tools/r8/shaking/ClassKindTest.java
index 89cbbbe..5eba7b5 100644
--- a/src/test/java/com/android/tools/r8/shaking/ClassKindTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ClassKindTest.java
@@ -33,7 +33,7 @@
}
@Parameters(name = "{0}")
- public static Collection<Object[]> paramters() {
+ public static Collection<Object[]> parameters() {
return ImmutableList.copyOf(new Object[][]{
{"-keep interface *", ImmutableList.of(Interface.class)},
{"-keep class *", CLASSES_TO_INCLUDE},
diff --git a/src/test/java/com/android/tools/r8/shaking/EnclosingMethodTest.java b/src/test/java/com/android/tools/r8/shaking/EnclosingMethodTest.java
index bebe66f..afc319a 100644
--- a/src/test/java/com/android/tools/r8/shaking/EnclosingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EnclosingMethodTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.Collection;
@@ -48,12 +49,7 @@
@Parameterized.Parameters(name = "Backend: {0} minification: {1}")
public static Collection<Object[]> data() {
- ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
- for (Backend backend : Backend.values()) {
- builder.add(new Object[]{backend, Boolean.TRUE});
- builder.add(new Object[]{backend, Boolean.FALSE});
- }
- return builder.build();
+ return buildParameters(Backend.values(), BooleanUtils.values());
}
public EnclosingMethodTest(Backend backend, boolean enableMinification) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdaReceiverTest.java b/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdaReceiverTest.java
new file mode 100644
index 0000000..225ddd6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdaReceiverTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+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 InstantiatedLambdaReceiverTest extends TestBase {
+
+ private Backend backend;
+
+ private static final String expectedOutput = "In C.m()";
+
+ @Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ public InstantiatedLambdaReceiverTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void jvmTest() throws Exception {
+ assumeTrue(
+ "JVM test independent of Art version - only run when testing on latest",
+ ToolHelper.getDexVm().getVersion().isLatest());
+ testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+ }
+
+ @Test
+ public void dexTest() throws Exception {
+ testForR8(backend)
+ .addInnerClasses(InstantiatedLambdaReceiverTest.class)
+ .addKeepMainRule(TestClass.class)
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ interface I {
+ void m();
+ }
+
+ interface II extends I {}
+
+ static class C implements I {
+
+ @Override
+ public void m() {
+ System.out.print("In C.m()");
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ I i = new C();
+ II x = i::m; // This should mark II as being instantiated!
+ x.m();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
index 2490d41..4be2aaf 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -45,15 +46,7 @@
@Parameterized.Parameters(name = "Backend: {0}, class inlining: {1}, vertical class merging: {2}")
public static Collection<Object[]> data() {
- ImmutableList.Builder<Object[]> builder = ImmutableList.builder();
- for (Backend backend : Backend.values()) {
- for (boolean enableClassInlining : ImmutableList.of(true, false)) {
- for (boolean enableVerticalClassMerging : ImmutableList.of(true, false)) {
- builder.add(new Object[] {backend, enableClassInlining, enableVerticalClassMerging});
- }
- }
- }
- return builder.build();
+ return buildParameters(Backend.values(), BooleanUtils.values(), BooleanUtils.values());
}
public NonVirtualOverrideTest(
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index 3b5a830..e5f3517 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.shaking.PrintUsageTest.PrintUsageInspector.ClassSubject;
import com.android.tools.r8.utils.ListUtils;
@@ -39,12 +40,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class PrintUsageTest {
-
- private enum Backend {
- CF,
- DEX
- }
+public class PrintUsageTest extends TestBase {
private static final String PRINT_USAGE_FILE_SUFFIX = "-print-usage.txt";
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index c268023..b302615 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
-import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -44,7 +44,7 @@
* that calls {@link TreeShakingTest::runTest}, passing in the path to your keep rule file and
* lambdas to determine if the right bits of the application are kept or discarded.
*/
-public abstract class TreeShakingTest {
+public abstract class TreeShakingTest extends TestBase {
private Path proguardMap;
private Path out;
@@ -53,10 +53,6 @@
DEX, JAR
}
- protected enum Backend {
- DEX, CF
- }
-
private final String name;
private final String mainClass;
private final Frontend frontend;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
index e420e1e..a603f92 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
@@ -11,13 +11,11 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
-import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java b/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
index d02fa5b..6aa68de 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
@@ -48,35 +49,30 @@
@RunWith(Parameterized.class)
public class IfRuleWithInlining extends ProguardCompatibilityTestBase {
- private final static List<Class> CLASSES = ImmutableList.of(
- A.class, D.class, Main.class);
+ private static final List<Class> CLASSES = ImmutableList.of(A.class, D.class, Main.class);
private final Shrinker shrinker;
- private final boolean inlineMethod;
+ private final boolean neverInlineMethod;
- public IfRuleWithInlining(Shrinker shrinker, boolean inlineMethod) {
+ public IfRuleWithInlining(Shrinker shrinker, boolean neverInlineMethod) {
this.shrinker = shrinker;
- this.inlineMethod = inlineMethod;
+ this.neverInlineMethod = neverInlineMethod;
}
- @Parameters(name = "shrinker: {0} inlineMethod: {1}")
+ @Parameters(name = "shrinker: {0} neverInlineMethod: {1}")
public static Collection<Object[]> data() {
// We don't run this on Proguard, as triggering inlining in Proguard is out of our control.
- return ImmutableList.of(
- new Object[] {Shrinker.R8, true},
- new Object[] {Shrinker.R8, false},
- new Object[] {Shrinker.R8_CF, true},
- new Object[] {Shrinker.R8_CF, false});
+ return buildParameters(
+ ImmutableList.of(Shrinker.R8, Shrinker.R8_CF), BooleanUtils.values());
}
private void check(AndroidApp app) throws Exception {
CodeInspector inspector = new CodeInspector(app);
ClassSubject clazzA = inspector.clazz(A.class);
assertThat(clazzA, isPresent());
- // A.a might be inlined.
- assertEquals(!inlineMethod, clazzA.method("int", "a", ImmutableList.of()).isPresent());
- // TODO(110148109): class D should be present - inlining or not.
- assertEquals(!inlineMethod, inspector.clazz(D.class).isPresent());
+ // A.a should not be inlined.
+ assertThat(clazzA.method("int", "a", ImmutableList.of()), isPresent());
+ assertThat(inspector.clazz(D.class), isPresent());
ProcessResult result;
if (shrinker == Shrinker.R8) {
result = runOnArtRaw(app, Main.class.getName());
@@ -87,22 +83,18 @@
result = ToolHelper.runJava(file, Main.class.getName());
}
assertEquals(0, result.exitCode);
- // TODO(110148109): Output should be the same - inlining or not.
- assertEquals(!inlineMethod ? "1" : "2", result.stdout);
+ assertEquals("1", result.stdout);
}
@Test
public void testMergedClassMethodInIfRule() throws Exception {
- List<String> config = ImmutableList.of(
- "-keep class **.Main { public static void main(java.lang.String[]); }",
- inlineMethod
- ? "-forceinline class **.A { int a(); }"
- : "-neverinline class **.A { int a(); }",
- "-if class **.A { static int a(); }",
- "-keep class **.D",
- "-dontobfuscate"
- );
-
+ List<String> config =
+ ImmutableList.of(
+ "-keep class **.Main { public static void main(java.lang.String[]); }",
+ neverInlineMethod ? "-neverinline class **.A { int a(); }" : "",
+ "-if class **.A { static int a(); }",
+ "-keep class **.D",
+ "-dontobfuscate");
check(runShrinker(shrinker, CLASSES, config));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
index 57ad556..2d9027d 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
@@ -8,14 +8,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ir.optimize.Inliner.Reason;
-import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
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.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.List;
import org.junit.Test;
@@ -23,6 +23,7 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
+@NeverClassInline
class A {
int x = 1;
int a() throws ClassNotFoundException {
@@ -34,6 +35,7 @@
}
}
+@NeverClassInline
class B extends A {
int y = 2;
int b() {
@@ -41,6 +43,7 @@
}
}
+@NeverClassInline
class C extends B {
int z = 3;
int c() {
@@ -48,8 +51,8 @@
}
}
-class D {
-}
+@NeverClassInline
+class D {}
class Main {
public static void main(String[] args) throws ClassNotFoundException {
@@ -61,7 +64,7 @@
@RunWith(Parameterized.class)
public class IfRuleWithVerticalClassMerging extends TestBase {
- private static final List<Class> CLASSES =
+ private static final List<Class<?>> CLASSES =
ImmutableList.of(A.class, B.class, C.class, D.class, Main.class);
private final Backend backend;
@@ -75,44 +78,20 @@
@Parameters(name = "Backend: {0}, vertical class merging: {1}")
public static Collection<Object[]> data() {
// We don't run this on Proguard, as Proguard does not merge A into B.
- return ImmutableList.of(
- new Object[] {Backend.DEX, true},
- new Object[] {Backend.DEX, false},
- new Object[] {Backend.CF, true},
- new Object[] {Backend.CF, false});
+ return buildParameters(Backend.values(), BooleanUtils.values());
}
private void configure(InternalOptions options) {
options.enableVerticalClassMerging = enableVerticalClassMerging;
-
- // TODO(b/110148109): Allow ordinary method inlining when -if rules work with inlining.
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- }
-
- private void check(AndroidApp app) throws Exception {
- CodeInspector inspector = new CodeInspector(app);
- ClassSubject clazzA = inspector.clazz(A.class);
- assertEquals(!enableVerticalClassMerging, clazzA.isPresent());
- ClassSubject clazzB = inspector.clazz(B.class);
- assertThat(clazzB, isPresent());
- ClassSubject clazzD = inspector.clazz(D.class);
- assertThat(clazzD, isPresent());
- assertEquals("123456", runOnVM(app, Main.class, backend));
}
@Test
public void testMergedClassInIfRule() throws Exception {
// Class C is kept, meaning that it will not be touched.
// Class A will be merged into class B.
- String config =
- String.join(
- System.lineSeparator(),
- "-keep class **.Main { public static void main(java.lang.String[]); }",
- "-keep class **.C",
- "-if class **.A",
- "-keep class **.D",
- "-dontobfuscate");
- check(compileWithR8(readClasses(CLASSES), config, this::configure, backend));
+ runTestWithProguardConfig(
+ StringUtils.lines(
+ "-keep class **.C", "-if class **.A", "-keep class **.D", "-dontobfuscate"));
}
@Test
@@ -120,15 +99,9 @@
// Class C is kept, meaning that it will not be touched.
// Class A will be merged into class B.
// Main.main access A.x, so that field exists satisfying the if rule.
- String config =
- String.join(
- System.lineSeparator(),
- "-keep class **.Main { public static void main(java.lang.String[]); }",
- "-keep class **.C",
- "-if class **.A { int x; }",
- "-keep class **.D",
- "-dontobfuscate");
- check(compileWithR8(readClasses(CLASSES), config, this::configure, backend));
+ runTestWithProguardConfig(
+ StringUtils.lines(
+ "-keep class **.C", "-if class **.A { int x; }", "-keep class **.D", "-dontobfuscate"));
}
@Test
@@ -136,14 +109,31 @@
// Class C is kept, meaning that it will not be touched.
// Class A will be merged into class B.
// Main.main access A.a(), that method exists satisfying the if rule.
- String config =
- String.join(
- System.lineSeparator(),
- "-keep class **.Main { public static void main(java.lang.String[]); }",
+ runTestWithProguardConfig(
+ StringUtils.lines(
"-keep class **.C",
"-if class **.A { int a(); }",
"-keep class **.D",
- "-dontobfuscate");
- check(compileWithR8(readClasses(CLASSES), config, this::configure, backend));
+ "-dontobfuscate"));
+ }
+
+ private void runTestWithProguardConfig(String config) throws Exception {
+ CodeInspector inspector =
+ testForR8(backend)
+ .addProgramClasses(CLASSES)
+ .addKeepMainRule(Main.class)
+ .addKeepRules(config)
+ .enableClassInliningAnnotations()
+ .addOptionsModification(this::configure)
+ .run(Main.class)
+ .assertSuccessWithOutput("123456")
+ .inspector();
+
+ ClassSubject clazzA = inspector.clazz(A.class);
+ assertEquals(!enableVerticalClassMerging, clazzA.isPresent());
+ ClassSubject clazzB = inspector.clazz(B.class);
+ assertThat(clazzB, isPresent());
+ ClassSubject clazzD = inspector.clazz(D.class);
+ assertThat(clazzD, isPresent());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
index 7c85a7b..879efe0 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
+import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
@@ -55,6 +56,7 @@
}
}
+ @NeverClassInline
static class TestClass extends SuperTestClass {
private A field = null;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedTypeBaseTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedTypeBaseTest.java
index cda8096..4fa2440 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedTypeBaseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedTypeBaseTest.java
@@ -5,18 +5,15 @@
package com.android.tools.r8.shaking.ifrule.verticalclassmerging;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ir.optimize.Inliner.Reason;
-import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.List;
import org.junit.Test;
@@ -42,7 +39,7 @@
static class Unused {}
final Backend backend;
- final List<Class> classes;
+ final List<Class<?>> classes;
final boolean enableVerticalClassMerging;
public MergedTypeBaseTest(Backend backend, boolean enableVerticalClassMerging) {
@@ -54,7 +51,7 @@
this.backend = backend;
this.enableVerticalClassMerging = enableVerticalClassMerging;
this.classes =
- ImmutableList.<Class>builder()
+ ImmutableList.<Class<?>>builder()
.add(A.class, B.class, C.class, I.class, J.class, K.class, Unused.class, getTestClass())
.addAll(additionalClasses)
.build();
@@ -63,11 +60,7 @@
@Parameters(name = "Backend: {0}, vertical class merging: {1}")
public static Collection<Object[]> data() {
// We don't run this on Proguard, as Proguard does not merge A into B.
- return ImmutableList.of(
- new Object[] {Backend.DEX, true},
- new Object[] {Backend.DEX, false},
- new Object[] {Backend.CF, true},
- new Object[] {Backend.CF, false});
+ return buildParameters(Backend.values(), BooleanUtils.values());
}
public abstract Class<?> getTestClass();
@@ -95,17 +88,18 @@
String expected = getExpectedStdout();
assertEquals(expected, runOnJava(getTestClass()));
- String config =
- StringUtils.joinLines(
- "-keep class " + getTestClass().getTypeName() + " {",
- " public static void main(java.lang.String[]);",
- "}",
+ testForR8(backend)
+ .addProgramClasses(classes)
+ .addKeepMainRule(getTestClass())
+ .addKeepRules(
getConditionForProguardIfRule(),
"-keep class " + Unused.class.getTypeName(),
- getAdditionalKeepRules());
- AndroidApp output = compileWithR8(readClasses(classes), config, this::configure, backend);
- assertEquals(expected, runOnVM(output, getTestClass(), backend));
- inspect(new CodeInspector(output));
+ getAdditionalKeepRules())
+ .addOptionsModification(this::configure)
+ .enableClassInliningAnnotations()
+ .run(getTestClass())
+ .assertSuccessWithOutput(expected)
+ .inspect(this::inspect);
}
private void configure(InternalOptions options) {
@@ -115,8 +109,5 @@
// To ensure that the handling of extends and implements rules work as intended,
// and that we don't end up keeping `Unused` only because one of the two implementations work.
options.testing.allowProguardRulesThatUseExtendsOrImplementsWrong = false;
-
- // TODO(b/110148109): Allow ordinary method inlining when -if rules work with inlining.
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
}
}
diff --git a/src/test/java/dalvik/annotation/optimization/ReachabilitySensitive.java b/src/test/java/dalvik/annotation/optimization/ReachabilitySensitive.java
new file mode 100644
index 0000000..04fc705
--- /dev/null
+++ b/src/test/java/dalvik/annotation/optimization/ReachabilitySensitive.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2018 the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package dalvik.annotation.optimization;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+// This is a copy of libcore/dalvik/annotation/optimiztion/ReachabilitySensitive.java.
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface ReachabilitySensitive {}