Merge "Add Proguard 6.0 to third_party"
diff --git a/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java b/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java
index 00cb542..0a633f3 100644
--- a/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java
+++ b/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java
@@ -316,14 +316,14 @@
@Override
public void writeResult() throws IOException {
if (dexPath != null) {
- Segment[] segments = DexFileReader.parseMapFrom(dexPath);
- for (Segment segment : segments) {
- int y = segment.offset / width;
+ DexSection[] sections = DexParser.parseMapFrom(dexPath);
+ for (DexSection section : sections) {
+ int y = section.offset / width;
for (int x = 0; x < width; x++) {
int val = (x / 10) % 2 == 0 ? 0 : 0xffffff;
image.setRGB(x, y, val);
}
- System.out.println(segment);
+ System.out.println(section);
}
}
ImageIO.write(image, "png", output.toFile());
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index a7bb291..4a26889 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -122,7 +122,7 @@
static void runForTesting(AndroidApp inputApp, InternalOptions options)
throws IOException, CompilationException {
- ExecutorService executor = ThreadUtils.getExecutorService(ThreadUtils.NOT_SPECIFIED);
+ ExecutorService executor = ThreadUtils.getExecutorService(options);
try {
run(inputApp, options, executor);
} finally {
@@ -146,13 +146,13 @@
private static void run(AndroidApp inputApp, InternalOptions options, ExecutorService executor)
throws IOException, CompilationException {
+ Timing timing = new Timing("D8");
try {
// Disable global optimizations.
options.enableMinification = false;
options.enableInlining = false;
options.outline.enabled = false;
- Timing timing = new Timing("DX timer");
DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
AppInfo appInfo = new AppInfo(app);
app = optimize(app, appInfo, options, timing, executor);
@@ -171,6 +171,10 @@
throw new AssertionError(e); // unwrapping method should have thrown
} finally {
options.signalFinishedToProgramConsumer();
+ // Dump timings.
+ if (options.printTimes) {
+ timing.report();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 5d77ad3..fed4a00 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "v1.1.7-dev";
+ public static final String LABEL = "v1.1.8-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
index 908372a..8c6574c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
@@ -11,6 +11,7 @@
public class DexDebugEntry {
+ public final boolean lineEntry;
public final int address;
public final int line;
public final DexString sourceFile;
@@ -21,6 +22,7 @@
public final Position callerPosition;
public DexDebugEntry(
+ boolean lineEntry,
int address,
int line,
DexString sourceFile,
@@ -29,6 +31,7 @@
ImmutableMap<Integer, DebugLocalInfo> locals,
DexMethod method,
Position callerPosition) {
+ this.lineEntry = lineEntry;
this.address = address;
this.line = line;
this.sourceFile = sourceFile;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index a1bc201..b672e26 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -98,6 +98,7 @@
@Override
public void visit(DexDebugEvent.AdvancePC advancePC) {
positionState.visit(advancePC);
+ entryEventReceived(false);
}
@Override
@@ -113,7 +114,7 @@
@Override
public void visit(DexDebugEvent.Default defaultEvent) {
positionState.visit(defaultEvent);
- defaultEventReceived();
+ entryEventReceived(true);
}
@Override
@@ -153,11 +154,12 @@
getEntry(restartLocal.registerNum).reset();
}
- public void defaultEventReceived() {
+ private void entryEventReceived(boolean lineEntry) {
if (pending != null) {
// Local changes contribute to the pending position entry.
entries.add(
new DexDebugEntry(
+ pending.lineEntry,
pending.address,
pending.line,
pending.sourceFile,
@@ -169,6 +171,7 @@
}
pending =
new DexDebugEntry(
+ lineEntry,
positionState.getCurrentPc(),
positionState.getCurrentLine(),
positionState.getCurrentFile(),
@@ -184,7 +187,7 @@
public List<DexDebugEntry> build() {
// Flush any pending entry.
if (pending != null) {
- defaultEventReceived(); // To flush 'pending'.
+ entryEventReceived(false); // To flush 'pending'.
pending = null;
}
List<DexDebugEntry> result = entries;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index bee0910..69fb348 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -216,7 +216,9 @@
private void emitLocalChanges(int pc) {
// If pc advanced since the locals changed and locals indeed have changed, emit the changes.
if (localsChanged()) {
- emitAdvancementEvents(emittedPc, emittedPosition, pc, emittedPosition, events, factory);
+ assert emittedPc != pc;
+ int pcDelta = emittedPc == NO_PC_INFO ? pc : pc - emittedPc;
+ events.add(factory.createAdvancePC(pcDelta));
emittedPc = pc;
emitLocalChangeEvents(emittedLocals, pendingLocals, lastKnownLocals, events, factory);
pendingLocalChanges = false;
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 bb4891b..ff8bd02 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
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -44,11 +45,15 @@
public final boolean hasDebugPositions;
+ private final InternalOptions options;
+
public IRCode(
+ InternalOptions options,
DexEncodedMethod method,
LinkedList<BasicBlock> blocks,
ValueNumberGenerator valueNumberGenerator,
boolean hasDebugPositions) {
+ this.options = options;
this.method = method;
this.blocks = blocks;
this.valueNumberGenerator = valueNumberGenerator;
@@ -251,7 +256,10 @@
ImmutableList.Builder<BasicBlock> builder = ImmutableList.builder();
BasicBlock entryBlock = blocks.getFirst();
depthFirstSorting(visitedBlock, entryBlock, builder);
- return builder.build().reverse();
+ ImmutableList<BasicBlock> ordered = builder.build().reverse();
+ return options.testing.placeExceptionalBlocksLast
+ ? reorderExceptionalBlocksLastForTesting(ordered)
+ : ordered;
}
private void depthFirstSorting(Set<BasicBlock> visitedBlock, BasicBlock block,
@@ -265,6 +273,23 @@
}
}
+ // Reorder the blocks forcing all exceptional blocks to be at the end.
+ private static ImmutableList<BasicBlock> reorderExceptionalBlocksLastForTesting(
+ ImmutableList<BasicBlock> blocks) {
+ ImmutableList.Builder<BasicBlock> reordered = ImmutableList.builder();
+ for (BasicBlock block : blocks) {
+ if (!block.entry().isMoveException()) {
+ reordered.add(block);
+ }
+ }
+ for (BasicBlock block : blocks) {
+ if (block.entry().isMoveException()) {
+ reordered.add(block);
+ }
+ }
+ return reordered.build();
+ }
+
public void print(CfgPrinter printer) {
ensureBlockNumbering();
for (BasicBlock block : blocks) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index eb58c4d..17ebfb5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
+import com.android.tools.r8.graph.AppInfo.ResolutionResult;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -118,23 +119,49 @@
if (targets == null || targets.isEmpty()) {
return Constraint.NEVER;
}
+
Constraint result = Constraint.ALWAYS;
+
+ // Perform resolution and derive inlining constraints based on the accessibility of the
+ // resolution result.
+ ResolutionResult resolutionResult = info.resolveMethod(method.holder, method);
+ DexEncodedMethod resolutionTarget = resolutionResult.asResultOfResolve();
+ if (resolutionTarget == null) {
+ // This will fail at runtime.
+ return Constraint.NEVER;
+ }
+ DexType methodHolder = resolutionTarget.method.holder;
+ DexClass methodClass = info.definitionFor(methodHolder);
+ assert methodClass != null;
+ Constraint methodConstraint = Constraint
+ .deriveConstraint(invocationContext, methodHolder, resolutionTarget.accessFlags, info);
+ result = Constraint.min(result, methodConstraint);
+ // We also have to take the constraint of the enclosing class of the resolution result
+ // into account. We do not allow inlining this method if it is calling something that
+ // is inaccessible. Inlining in that case could move the code to another package making a
+ // call succeed that should not succeed. Conversely, if the resolution result is accessible,
+ // we have to make sure that inlining cannot make it inaccessible.
+ Constraint classConstraint = Constraint
+ .deriveConstraint(invocationContext, methodHolder, methodClass.accessFlags, info);
+ result = Constraint.min(result, classConstraint);
+ if (result == Constraint.NEVER) {
+ return result;
+ }
+
+ // For each of the actual potential targets, derive constraints based on the accessibility
+ // of the method itself.
for (DexEncodedMethod target : targets) {
- DexType methodHolder = target.method.holder;
- DexClass methodClass = info.definitionFor(methodHolder);
- if ((methodClass != null)) {
- Constraint methodConstraint = Constraint
- .deriveConstraint(invocationContext, methodHolder, target.accessFlags, info);
- result = Constraint.min(result, methodConstraint);
- // We also have to take the constraint of the enclosing class into account.
- Constraint classConstraint = Constraint
- .deriveConstraint(invocationContext, methodHolder, methodClass.accessFlags, info);
- result = Constraint.min(result, classConstraint);
- }
+ methodHolder = target.method.holder;
+ methodClass = info.definitionFor(methodHolder);
+ assert methodClass != null;
+ methodConstraint = Constraint
+ .deriveConstraint(invocationContext, methodHolder, target.accessFlags, info);
+ result = Constraint.min(result, methodConstraint);
if (result == Constraint.NEVER) {
- break;
+ return result;
}
}
+
return result;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index baf97f6..82840f8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -52,6 +52,7 @@
@Override
public void buildDex(DexBuilder builder) {
+ assert builder.getOptions().canUseNotInstruction();
com.android.tools.r8.code.Instruction instruction;
int dest = builder.allocatedRegister(dest(), getNumber());
int src = builder.allocatedRegister(source(), getNumber());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 140184d..55e4b83 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -234,7 +234,7 @@
currentPosition = Position.none();
} else {
currentPosition = getCanonicalPositionAppendCaller(current);
- if (current.address == offset) {
+ if (current.lineEntry && current.address == offset) {
builder.addDebugPosition(currentPosition);
}
}
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 0a27017..98c1015 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
@@ -390,7 +390,7 @@
joinPredecessorsWithIdenticalPhis();
// Package up the IR code.
- IRCode ir = new IRCode(method, blocks, valueNumberGenerator, hasDebugPositions);
+ IRCode ir = new IRCode(options, method, blocks, valueNumberGenerator, hasDebugPositions);
// Split critical edges to make sure that we have a place to insert phi moves if
// necessary.
@@ -1258,7 +1258,13 @@
public void addNot(NumericType type, int dest, int value) {
Value in = readNumericRegister(value, type);
Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
- Not instruction = new Not(type, out, in);
+ Instruction instruction;
+ if (options.canUseNotInstruction()) {
+ instruction = new Not(type, out, in);
+ } else {
+ Value minusOne = readLiteral(ValueType.fromNumericType(type), -1);
+ instruction = new Xor(type, out, in, minusOne);
+ }
assert !instruction.instructionTypeCanThrow();
addInstruction(instruction);
}
@@ -1616,6 +1622,22 @@
return readRegister(register, ValueType.fromNumericType(type));
}
+ public Value readLiteral(ValueType type, long constant) {
+ if (type == ValueType.INT) {
+ return readIntLiteral(constant);
+ } else {
+ assert type == ValueType.LONG;
+ return readLongLiteral(constant);
+ }
+ }
+
+ public Value readLongLiteral(long constant) {
+ Value value = new Value(valueNumberGenerator.next(), ValueType.LONG, null);
+ ConstNumber number = new ConstNumber(value, constant);
+ add(number);
+ return number.outValue();
+ }
+
public Value readIntLiteral(long constant) {
Value value = new Value(valueNumberGenerator.next(), ValueType.INT, null);
ConstNumber number = new ConstNumber(value, constant);
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 bb4b629..39cea77 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
@@ -229,6 +229,7 @@
throws ExecutionException, ApiLevelException {
removeLambdaDeserializationMethods();
+ timing.begin("IR conversion");
convertClassesToDex(application.classes(), executor);
// Build a new application with jumbo string info,
@@ -239,6 +240,7 @@
desugarInterfaceMethods(builder, ExcludeDexResources);
handleSynthesizedClassMapping(builder);
+ timing.end();
return builder.build();
}
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index e517da9..53416c6 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
@@ -23,8 +22,8 @@
}
@Override
- Function<DexType, ?> getKeyTransform(ProguardConfiguration config) {
- if (config.isOverloadAggressively()) {
+ Function<DexType, ?> getKeyTransform() {
+ if (overloadAggressively) {
// Use the type as the key, hence reuse names per type.
return a -> a;
} else {
@@ -88,9 +87,9 @@
private void renameField(DexEncodedField encodedField, NamingState<DexType, ?> state) {
DexField field = encodedField.field;
if (!state.isReserved(field.name, field.type)) {
- DexString candidate = state.assignNewNameFor(field.name, field.type, false);
- renaming.put(field, candidate);
- state.addRenaming(field.name, field.type, candidate);
+ renaming.put(
+ field,
+ state.assignNewNameFor(field.name, field.type, useUniqueMemberNames));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
index 4a288be..1fa8aaa 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.CachedHashValueDexItem;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
@@ -25,17 +24,19 @@
protected final Map<DexType, NamingState<StateType, ?>> states = new IdentityHashMap<>();
protected final NamingState<StateType, ?> globalState;
protected final boolean useUniqueMemberNames;
+ protected final boolean overloadAggressively;
MemberNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
this.appInfo = appInfo;
this.rootSet = rootSet;
this.dictionary = options.proguardConfiguration.getObfuscationDictionary();
- this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary,
- getKeyTransform(options.proguardConfiguration));
this.useUniqueMemberNames = options.proguardConfiguration.isUseUniqueClassMemberNames();
+ this.overloadAggressively = options.proguardConfiguration.isOverloadAggressively();
+ this.globalState =
+ NamingState.createRoot(appInfo.dexItemFactory, dictionary, getKeyTransform());
}
- abstract Function<StateType, ?> getKeyTransform(ProguardConfiguration config);
+ abstract Function<StateType, ?> getKeyTransform();
protected NamingState<StateType, ?> computeStateIfAbsent(
DexType type, Function<DexType, NamingState<StateType, ?>> f) {
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 7b939ae..936a06a 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.MethodJavaSignatureEquivalence;
@@ -90,19 +89,17 @@
class MethodNameMinifier extends MemberNameMinifier<DexMethod, DexProto> {
private final Equivalence<DexMethod> equivalence;
- private final ProguardConfiguration config;
MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
super(appInfo, rootSet, options);
- this.config = options.proguardConfiguration;
- equivalence = config.isOverloadAggressively()
+ equivalence = overloadAggressively
? MethodSignatureEquivalence.get()
: MethodJavaSignatureEquivalence.get();
}
@Override
- Function<DexProto, ?> getKeyTransform(ProguardConfiguration config) {
- if (config.isOverloadAggressively()) {
+ Function<DexProto, ?> getKeyTransform() {
+ if (overloadAggressively) {
// Use the full proto as key, hence reuse names based on full signature.
return a -> a;
} else {
@@ -334,8 +331,7 @@
computeStateIfAbsent(
libraryFrontier,
ignore -> parent == null
- ? NamingState
- .createRoot(appInfo.dexItemFactory, dictionary, getKeyTransform(config))
+ ? NamingState.createRoot(appInfo.dexItemFactory, dictionary, getKeyTransform())
: parent.createChild());
if (holder != null) {
boolean keepAll = holder.isLibraryClass() || holder.accessFlags.isAnnotation();
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 902e378..6fc9e17 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -76,7 +76,7 @@
}
}
- public boolean printTimes = false;
+ public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
// Flag to toggle if DEX code objects should pass-through without IR processing.
public boolean passthroughDexCode = false;
@@ -392,6 +392,7 @@
Function.identity();
public boolean invertConditionals = false;
+ public boolean placeExceptionalBlocksLast = false;
}
public boolean canUseInvokePolymorphicOnVarHandle() {
diff --git a/src/test/examples/inlining/IFace.java b/src/test/examples/inlining/IFace.java
new file mode 100644
index 0000000..d0402f2
--- /dev/null
+++ b/src/test/examples/inlining/IFace.java
@@ -0,0 +1,8 @@
+// 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 inlining;
+
+public interface IFace {
+ int foo();
+}
diff --git a/src/test/examples/inlining/Inlining.java b/src/test/examples/inlining/Inlining.java
index 77b2979..0705587 100644
--- a/src/test/examples/inlining/Inlining.java
+++ b/src/test/examples/inlining/Inlining.java
@@ -4,6 +4,7 @@
package inlining;
import inlining.Nullability.Factor;
+import inlining.pkg.InterfaceImplementationContainer;
import inlining.pkg.OtherPublicClass;
import inlining.pkg.PublicClass;
import inlining.pkg.Subclass;
@@ -211,6 +212,8 @@
} catch (Throwable unexpected) {
System.out.println("Unexpected exception for notInlinableOnThrow");
}
+
+ System.out.println(callInterfaceMethod(InterfaceImplementationContainer.getIFace()));
}
private static boolean intCmpExpression(A a, A b) {
@@ -218,6 +221,11 @@
}
@CheckDiscarded
+ private static int callInterfaceMethod(IFace i) {
+ return i.foo();
+ }
+
+ @CheckDiscarded
private static int intConstantInline() {
return 42;
}
diff --git a/src/test/examples/inlining/pkg/InterfaceImplementationContainer.java b/src/test/examples/inlining/pkg/InterfaceImplementationContainer.java
new file mode 100644
index 0000000..5135fa2
--- /dev/null
+++ b/src/test/examples/inlining/pkg/InterfaceImplementationContainer.java
@@ -0,0 +1,19 @@
+// 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 inlining.pkg;
+
+import inlining.IFace;
+
+public class InterfaceImplementationContainer {
+ private static class IFaceImplementation implements IFace {
+ @Override
+ public int foo() {
+ return 42;
+ }
+ }
+
+ public static IFace getIFace() {
+ return new IFaceImplementation();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTest.java b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTest.java
new file mode 100644
index 0000000..0142dba
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTest.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+public class BreakAtTryAndCatchTest {
+ int i = 0;
+
+ void foo() {
+ try { bar(); } catch (RuntimeException e) { baz(); }
+ baz();
+ }
+
+ int bar() {
+ if (i++ % 2 == 0) {
+ System.out.println("bar return " + i);
+ return i;
+ }
+ System.out.println("bar throw " + i);
+ throw new RuntimeException("" + i);
+ }
+
+ void baz() {
+ System.out.println("baz");
+ }
+
+ public static void main(String[] args) {
+ BreakAtTryAndCatchTest test = new BreakAtTryAndCatchTest();
+ test.foo();
+ test.foo();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTestRunner.java b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTestRunner.java
new file mode 100644
index 0000000..ca2f53f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTestRunner.java
@@ -0,0 +1,67 @@
+// 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.debug;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class BreakAtTryAndCatchTestRunner extends DebugTestBase {
+
+ private static final Class CLASS = BreakAtTryAndCatchTest.class;
+ private static final String FILE = CLASS.getSimpleName() + ".java";
+ private static final String NAME = CLASS.getCanonicalName();
+
+ private final String name;
+ private final DebugTestConfig config;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> setup() {
+ DelayedDebugTestConfig cf =
+ temp -> new CfDebugTestConfig().addPaths(ToolHelper.getClassPathForTests());
+ DelayedDebugTestConfig d8 =
+ temp -> new D8DebugTestConfig().compileAndAddClasses(temp, CLASS);
+ DelayedDebugTestConfig d8Reordered =
+ temp -> new D8DebugTestConfig().compileAndAdd(
+ temp,
+ Collections.singletonList(ToolHelper.getClassFileForTestClass(CLASS)),
+ options -> options.testing.placeExceptionalBlocksLast = true);
+ return ImmutableList.of(
+ new Object[]{"CF", cf},
+ new Object[]{"D8", d8},
+ new Object[]{"D8/reorder", d8Reordered}
+ );
+ }
+
+ public BreakAtTryAndCatchTestRunner(String name, DelayedDebugTestConfig config) {
+ this.name = name;
+ this.config = config.getConfig(temp);
+ }
+
+ @Test
+ public void testHitOnEntryOnly() throws Throwable {
+ Assume.assumeFalse("b/72933440", name.equals("D8/reorder"));
+ Assume.assumeFalse("b/73803266",
+ name.equals("D8") && ToolHelper.getDexVm() == DexVm.ART_6_0_1_HOST);
+ runDebugTest(
+ config,
+ NAME,
+ breakpoint(NAME, "foo", 10),
+ run(),
+ checkLine(FILE, 10), // hit line entry, bar does not throw
+ run(),
+ checkLine(FILE, 10), // hit line entry, bar does throw
+ breakpoint(NAME, "main", 31),
+ run(),
+ checkLine(FILE, 31), // No more hits on line, continue to main.
+ run());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java b/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java
index b588590..9fe7e23 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.debug;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Assume;
@@ -18,6 +19,7 @@
private static final String FILE = CLASS.getSimpleName() + ".java";
private static final String NAME = CLASS.getCanonicalName();
+ private final String name;
private final DebugTestConfig config;
@Parameterized.Parameters(name = "{0}")
@@ -30,13 +32,15 @@
}
public LocalChangeOnSameLineTestRunner(String name, DelayedDebugTestConfig config) {
+ this.name = name;
this.config = config.getConfig(temp);
}
/** Test that only hit the break point at line 15 once. */
@Test
public void testHitBreakpointOnce() throws Throwable {
- Assume.assumeFalse("b/72933440 : invalid line info table", config instanceof D8DebugTestConfig);
+ Assume.assumeFalse("b/73803266",
+ name.equals("D8") && ToolHelper.getDexVm() == DexVm.ART_6_0_1_HOST);
runDebugTest(
config,
NAME,
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
index 5d15735..4ce3b5a 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -125,14 +125,14 @@
}
private void checkConsistentEntries() {
- DexDebugEntry previousEntry = null;
+ DexDebugEntry previousLineEntry = null;
for (DexDebugEntry entry : entries) {
- if (previousEntry != null) {
+ if (entry.lineEntry) {
assertTrue(
"More than one entry defined for PC " + StringUtils.hexString(entry.address, 2),
- entry.address > previousEntry.address);
+ previousLineEntry == null || entry.address > previousLineEntry.address);
+ previousLineEntry = entry;
}
- previousEntry = entry;
}
}
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 d6f86d8..2da7d8c 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
@@ -123,9 +123,10 @@
LinkedList<BasicBlock> blocks = new LinkedList<>();
blocks.add(block);
- IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+ InternalOptions options = new InternalOptions();
+ IRCode code = new IRCode(options, null, blocks, new ValueNumberGenerator(), false);
PeepholeOptimizer.optimize(code,
- new MockLinearScanRegisterAllocator(code, new InternalOptions()));
+ new MockLinearScanRegisterAllocator(code, options));
// Check that all four constant number instructions remain.
assertEquals(4,
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 9c03ee7..1c72986 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
@@ -39,6 +39,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -111,9 +112,13 @@
assertEquals(javaResult.stdout, artOutput);
}
- private void checkAbsent(ClassSubject clazz, String name) {
+ private void checkAbsentBooleanMethod(ClassSubject clazz, String name) {
+ checkAbsent(clazz, "boolean", name, Collections.emptyList());
+ }
+
+ private void checkAbsent(ClassSubject clazz, String returnType, String name, List<String> args) {
assertTrue(clazz.isPresent());
- MethodSubject method = clazz.method("boolean", name, Collections.emptyList());
+ MethodSubject method = clazz.method(returnType, name, args);
assertFalse(method.isPresent());
}
@@ -134,20 +139,24 @@
DexInspector inspector = new DexInspector(getGeneratedDexFile().toAbsolutePath(),
getGeneratedProguardMap());
ClassSubject clazz = inspector.clazz("inlining.Inlining");
+
// Simple constant inlining.
- checkAbsent(clazz, "longExpression");
- checkAbsent(clazz, "intExpression");
- checkAbsent(clazz, "doubleExpression");
- checkAbsent(clazz, "floatExpression");
+ checkAbsentBooleanMethod(clazz, "longExpression");
+ checkAbsentBooleanMethod(clazz, "intExpression");
+ checkAbsentBooleanMethod(clazz, "doubleExpression");
+ checkAbsentBooleanMethod(clazz, "floatExpression");
// Simple return argument inlining.
- checkAbsent(clazz, "longArgumentExpression");
- checkAbsent(clazz, "intArgumentExpression");
- checkAbsent(clazz, "doubleArgumentExpression");
- checkAbsent(clazz, "floatArgumentExpression");
+ checkAbsentBooleanMethod(clazz, "longArgumentExpression");
+ checkAbsentBooleanMethod(clazz, "intArgumentExpression");
+ checkAbsentBooleanMethod(clazz, "doubleArgumentExpression");
+ checkAbsentBooleanMethod(clazz, "floatArgumentExpression");
+ // Static method calling interface method. The interface method implementation is in
+ // a private class in another package.
+ checkAbsent(clazz, "int", "callInterfaceMethod", ImmutableList.of("inlining.IFace"));
clazz = inspector.clazz("inlining.Nullability");
- checkAbsent(clazz, "inlinableWithPublicField");
- checkAbsent(clazz, "inlinableWithControlFlow");
+ checkAbsentBooleanMethod(clazz, "inlinableWithPublicField");
+ checkAbsentBooleanMethod(clazz, "inlinableWithControlFlow");
}
@Test
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 699ebb4..6e02a17 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
@@ -62,7 +62,7 @@
// 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.
- IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+ IRCode code = new IRCode(null, null, blocks, new ValueNumberGenerator(), false);
CodeRewriter.collapsTrivialGotos(null, code);
assertTrue(code.blocks.get(0).isTrivialGoto());
assertTrue(blocks.contains(block0));
@@ -130,7 +130,7 @@
// 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.
- IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+ IRCode code = new IRCode(null, null, blocks, new ValueNumberGenerator(), false);
CodeRewriter.collapsTrivialGotos(null, code);
assertTrue(block0.getInstructions().get(1).isIf());
assertEquals(block1, block0.getInstructions().get(1).asIf().fallthroughBlock());
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index 4362d7d..64d092b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.utils.StringUtils.BraceType;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import jasmin.ClassFile;
import java.io.ByteArrayOutputStream;
import java.io.StringReader;
@@ -144,6 +145,10 @@
return addMethod("static", name, argumentTypes, returnType, lines);
}
+ public MethodSignature addMainMethod(Iterable<String> lines) {
+ return addMainMethod(Iterables.toArray(lines, String.class));
+ }
+
public MethodSignature addMainMethod(String... lines) {
return addStaticMethod("main", ImmutableList.of("[Ljava/lang/String;"), "V", lines);
}
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index af8382e..bdedf43 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -116,9 +116,8 @@
static final Map<String, TestPredicate> FAILING_TESTS =
ImmutableMap.<String, TestPredicate>builder()
- // ART line-table currently returns too many line entries.
- .put("LineTableDuplicatesTest", RunJdwpTests::isJava)
// Other failures on various older runtimes.
+ .put("LineTableDuplicatesTest", or(RunJdwpTests::isJava, RunJdwpTests::isAndroidNOrAbove))
.put("ArrayReference.GetValuesTest", RunJdwpTests::isAndroidLOrAbove)
.put("ArrayReference.LengthTest", RunJdwpTests::isAndroidLOrAbove)
.put("ArrayReference.SetValues003Test", RunJdwpTests::isAndroidNOrAbove)
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
index 365ae83..51523f2 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.utils.DexInspector.FieldSubject;
import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -33,6 +34,7 @@
@RunWith(Parameterized.class)
public class ValidNameConflictTest extends JasminTestBase {
private final String CLASS_NAME = "Example";
+ private final String SUPER_CLASS = "Super";
private final String ANOTHER_CLASS = "Test";
private final String MSG = "Expected to be seen at the end.";
@@ -53,20 +55,15 @@
return testCases;
}
- private JasminBuilder buildFieldNameConflictClassFile() throws Exception {
- JasminBuilder builder = new JasminBuilder();
- ClassBuilder classBuilder = builder.addClass(CLASS_NAME);
- classBuilder.addStaticField("same", "Ljava/lang/Object;", null);
- classBuilder.addStaticField("same", "Ljava/lang/String;", "\"" + MSG + "\"");
- classBuilder.addMainMethod(
- ".limit stack 3",
- ".limit locals 4",
- " ldc Example",
- " invokevirtual java/lang/Class/getDeclaredFields()[Ljava/lang/reflect/Field;",
- " astore_0", // Field[]
+ private Iterable<String> buildCodeForVisitingDeclaredMembers(
+ Iterable<String> prologue, Iterable<String> argumentLoadingAndCall) {
+ return Iterables.concat(
+ prologue,
+ ImmutableList.of(
+ " astore_0", // Member[]
" aload_0",
" arraylength",
- " istore_1", // Field[].length
+ " istore_1", // Member[].length
" iconst_0",
" istore_2", // counter
"loop:",
@@ -75,22 +72,39 @@
" if_icmpge end",
" aload_0",
" iload_2",
- " aaload", // Field[counter]
+ " aaload", // Member[counter]
" astore_3",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
- " aload_3",
- " aconst_null",
- " invokevirtual java/lang/reflect/Field/get(Ljava/lang/Object;)Ljava/lang/Object;",
+ " aload_3"),
+ argumentLoadingAndCall,
+ ImmutableList.of(
" invokevirtual java/io/PrintStream/println(Ljava/lang/Object;)V",
" iinc 2 1", // counter++
" goto loop",
"end:",
- " return");
+ " return"));
+ }
+
+ private JasminBuilder buildFieldNameConflictClassFile() {
+ JasminBuilder builder = new JasminBuilder();
+ ClassBuilder classBuilder = builder.addClass(CLASS_NAME);
+ classBuilder.addStaticField("same", "Ljava/lang/Object;", null);
+ classBuilder.addStaticField("same", "Ljava/lang/String;", "\"" + MSG + "\"");
+ classBuilder.addMainMethod(
+ buildCodeForVisitingDeclaredMembers(
+ ImmutableList.of(
+ ".limit stack 3",
+ ".limit locals 4",
+ " ldc " + CLASS_NAME,
+ " invokevirtual java/lang/Class/getDeclaredFields()[Ljava/lang/reflect/Field;"),
+ ImmutableList.of(
+ " aconst_null",
+ " invokevirtual java/lang/reflect/Field/get(Ljava/lang/Object;)Ljava/lang/Object;")));
return builder;
}
@Test
- public void remainFieldNameConflictDueToKeepRules() throws Exception {
+ public void remainFieldNameConflict_keepRules() throws Exception {
Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -123,7 +137,7 @@
@Test
- public void remainFieldNameConflictWithUseUniqueClassMemberNames() throws Exception {
+ public void remainFieldNameConflict_useuniqueclassmembernames() throws Exception {
Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -152,7 +166,38 @@
}
@Test
- public void resolveFieldNameConflictWithoutAnyOption() throws Exception {
+ public void remainFieldNameConflict_useuniqueclassmembernames_overloadaggressively()
+ throws Exception {
+ Assume.assumeTrue(ToolHelper.artSupported());
+ JasminBuilder builder = buildFieldNameConflictClassFile();
+ ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+ assertEquals(0, javaOutput.exitCode);
+
+ List<String> pgConfigs = ImmutableList.of(
+ keepMainProguardConfiguration(CLASS_NAME),
+ "-useuniqueclassmembernames",
+ "-overloadaggressively", // no-op
+ "-dontshrink");
+ AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject clazz = dexInspector.clazz(CLASS_NAME);
+ assertTrue(clazz.isPresent());
+ FieldSubject f1 = clazz.field("java.lang.String", "same");
+ assertTrue(f1.isPresent());
+ assertTrue(f1.isRenamed());
+ FieldSubject f2 = clazz.field("java.lang.Object", "same");
+ assertTrue(f2.isPresent());
+ assertTrue(f2.isRenamed());
+ assertEquals(f1.getField().field.name, f2.getField().field.name);
+
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ assertEquals(0, artOutput.exitCode);
+ assertEquals(javaOutput.stdout, artOutput.stdout);
+ }
+
+ @Test
+ public void resolveFieldNameConflict_no_options() throws Exception {
Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -172,8 +217,7 @@
FieldSubject f2 = clazz.field("java.lang.Object", "same");
assertTrue(f2.isPresent());
assertTrue(f2.isRenamed());
- // TODO(b/73149686): R8 should be able to fix this conflict w/o -overloadaggressively.
- assertEquals(f1.getField().field.name, f2.getField().field.name);
+ assertNotEquals(f1.getField().field.name, f2.getField().field.name);
ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
assertEquals(0, artOutput.exitCode);
@@ -181,7 +225,7 @@
}
@Test
- public void resolveFieldNameConflictEvenWithOverloadAggressively() throws Exception {
+ public void remainFieldNameConflict_overloadaggressively() throws Exception {
Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -202,7 +246,6 @@
FieldSubject f2 = clazz.field("java.lang.Object", "same");
assertTrue(f2.isPresent());
assertTrue(f2.isRenamed());
- // TODO(b/72858955): R8 should resolve this field name conflict.
assertEquals(f1.getField().field.name, f2.getField().field.name);
ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
@@ -210,7 +253,7 @@
assertEquals(javaOutput.stdout, artOutput.stdout);
}
- private JasminBuilder buildMethodNameConflictClassFile() throws Exception {
+ private JasminBuilder buildMethodNameConflictClassFile() {
JasminBuilder builder = new JasminBuilder();
ClassBuilder classBuilder = builder.addClass(ANOTHER_CLASS);
classBuilder.addStaticMethod("same", ImmutableList.of(), "Ljava/lang/Object;",
@@ -221,41 +264,23 @@
"areturn");
classBuilder = builder.addClass(CLASS_NAME);
classBuilder.addMainMethod(
- ".limit stack 3",
- ".limit locals 4",
- " ldc Test",
- " invokevirtual java/lang/Class/getDeclaredMethods()[Ljava/lang/reflect/Method;",
- " astore_0", // Method[]
- " aload_0",
- " arraylength",
- " istore_1", // Method[].length
- " iconst_0",
- " istore_2", // counter
- "loop:",
- " iload_2",
- " iload_1",
- " if_icmpge end",
- " aload_0",
- " iload_2",
- " aaload", // Method[counter]
- " astore_3",
- " getstatic java/lang/System/out Ljava/io/PrintStream;",
- " aload_3",
- " aconst_null",
- " aconst_null",
- " checkcast [Ljava/lang/Object;",
- " invokevirtual java/lang/reflect/Method/invoke"
- + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
- " invokevirtual java/io/PrintStream/println(Ljava/lang/Object;)V",
- " iinc 2 1", // counter++
- " goto loop",
- "end:",
- " return");
+ buildCodeForVisitingDeclaredMembers(
+ ImmutableList.of(
+ ".limit stack 3",
+ ".limit locals 4",
+ " ldc " + ANOTHER_CLASS,
+ " invokevirtual java/lang/Class/getDeclaredMethods()[Ljava/lang/reflect/Method;"),
+ ImmutableList.of(
+ " aconst_null",
+ " aconst_null",
+ " checkcast [Ljava/lang/Object;",
+ " invokevirtual java/lang/reflect/Method/invoke"
+ + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")));
return builder;
}
@Test
- public void remainMethodNameConflictDueToKeepRules() throws Exception {
+ public void remainMethodNameConflict_keepRules() throws Exception {
Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -287,7 +312,7 @@
}
@Test
- public void remainMethodNameConflictWithUseUniqueClassMemberNames() throws Exception {
+ public void remainMethodNameConflict_useuniqueclassmembernames() throws Exception {
Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -316,7 +341,39 @@
}
@Test
- public void resolveMethodNameConflictWithoutAnyOption() throws Exception {
+ public void remainMethodNameConflict_useuniqueclassmembernames_overloadaggressively()
+ throws Exception {
+ Assume.assumeTrue(ToolHelper.artSupported());
+ JasminBuilder builder = buildMethodNameConflictClassFile();
+ ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+ assertEquals(0, javaOutput.exitCode);
+
+ List<String> pgConfigs = ImmutableList.of(
+ keepMainProguardConfiguration(CLASS_NAME),
+ "-useuniqueclassmembernames",
+ "-overloadaggressively", // no-op
+ "-dontshrink");
+ AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject clazz = dexInspector.clazz(ANOTHER_CLASS);
+ assertTrue(clazz.isPresent());
+ MethodSubject m1 = clazz.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(m1.isPresent());
+ assertTrue(m1.isRenamed());
+ MethodSubject m2 = clazz.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(m2.isPresent());
+ assertTrue(m2.isRenamed());
+ assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ assertEquals(0, artOutput.exitCode);
+ assertEquals(javaOutput.stdout, artOutput.stdout);
+ }
+
+
+ @Test
+ public void resolveMethodNameConflict_no_options() throws Exception {
Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -345,7 +402,7 @@
}
@Test
- public void resolveMethodNameConflictEvenWithOverloadAggressively() throws Exception {
+ public void remainMethodNameConflict_overloadaggressively() throws Exception {
Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -366,7 +423,6 @@
MethodSubject m2 = clazz.method("java.lang.Object", "same", ImmutableList.of());
assertTrue(m2.isPresent());
assertTrue(m2.isRenamed());
- // TODO(b/73149686): R8 should be able to fix this conflict even w/ -overloadaggressively.
assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
@@ -374,4 +430,265 @@
assertEquals(javaOutput.stdout, artOutput.stdout);
}
+ private JasminBuilder buildMethodNameConflictInHierarchy() {
+ JasminBuilder builder = new JasminBuilder();
+ ClassBuilder classBuilder = builder.addClass(SUPER_CLASS);
+ classBuilder.addVirtualMethod("same", ImmutableList.of(), "Ljava/lang/Object;",
+ "aconst_null",
+ "areturn");
+ classBuilder.addVirtualMethod("same", ImmutableList.of(), "Ljava/lang/String;",
+ "ldc \"" + MSG + "\"",
+ "areturn");
+ classBuilder = builder.addClass(ANOTHER_CLASS, SUPER_CLASS);
+ classBuilder.addVirtualMethod("same", ImmutableList.of(), "Ljava/lang/Object;",
+ "aload_0",
+ "invokespecial " + SUPER_CLASS + "/same()Ljava/lang/Object;",
+ "areturn");
+ classBuilder.addVirtualMethod("same", ImmutableList.of(), "Ljava/lang/String;",
+ "aload_0",
+ "invokespecial " + SUPER_CLASS + "/same()Ljava/lang/String;",
+ "areturn");
+ classBuilder = builder.addClass(CLASS_NAME);
+ classBuilder.addMainMethod(
+ buildCodeForVisitingDeclaredMembers(
+ ImmutableList.of(
+ ".limit stack 3",
+ ".limit locals 5",
+ " new " + ANOTHER_CLASS,
+ " dup",
+ " invokespecial " + ANOTHER_CLASS + "/<init>()V",
+ " astore 4",
+ " ldc " + ANOTHER_CLASS,
+ " invokevirtual java/lang/Class/getDeclaredMethods()[Ljava/lang/reflect/Method;"),
+ ImmutableList.of(
+ " aload 4", // instance
+ " aconst_null",
+ " checkcast [Ljava/lang/Object;",
+ " invokevirtual java/lang/reflect/Method/invoke"
+ + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")));
+ return builder;
+ }
+
+ @Test
+ public void remainMethodNameConflictInHierarchy_keepRules() throws Exception {
+ Assume.assumeTrue(ToolHelper.artSupported());
+ JasminBuilder builder = buildMethodNameConflictInHierarchy();
+ ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+ assertEquals(0, javaOutput.exitCode);
+
+ List<String> pgConfigs = ImmutableList.of(
+ "-keep class " + ANOTHER_CLASS + " {\n"
+ + " <methods>;"
+ + "}\n",
+ keepMainProguardConfiguration(CLASS_NAME),
+ "-useuniqueclassmembernames",
+ "-dontshrink");
+ AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+ assertTrue(sup.isPresent());
+ MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(m1.isPresent());
+ assertFalse(m1.isRenamed());
+ MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(m2.isPresent());
+ assertFalse(m2.isRenamed());
+ assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+ ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+ assertTrue(sub.isPresent());
+ MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(subM1.isPresent());
+ assertFalse(subM1.isRenamed());
+ MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(subM2.isPresent());
+ assertFalse(subM2.isRenamed());
+ assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+ // No matter what, overloading methods should be renamed to the same name.
+ assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+ assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ assertEquals(0, artOutput.exitCode);
+ assertEquals(javaOutput.stdout, artOutput.stdout);
+ }
+
+ @Test
+ public void remainMethodNameConflictInHierarchy_useuniqueclassmembernames() throws Exception {
+ Assume.assumeTrue(ToolHelper.artSupported());
+ JasminBuilder builder = buildMethodNameConflictInHierarchy();
+ ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+ assertEquals(0, javaOutput.exitCode);
+
+ List<String> pgConfigs = ImmutableList.of(
+ keepMainProguardConfiguration(CLASS_NAME),
+ "-useuniqueclassmembernames",
+ "-dontshrink");
+ AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+ assertTrue(sup.isPresent());
+ MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(m1.isPresent());
+ assertTrue(m1.isRenamed());
+ MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(m2.isPresent());
+ assertTrue(m2.isRenamed());
+ assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+ ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+ assertTrue(sub.isPresent());
+ MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(subM1.isPresent());
+ assertTrue(subM1.isRenamed());
+ MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(subM2.isPresent());
+ assertTrue(subM2.isRenamed());
+ assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+ // No matter what, overloading methods should be renamed to the same name.
+ assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+ assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ assertEquals(0, artOutput.exitCode);
+ assertEquals(javaOutput.stdout, artOutput.stdout);
+ }
+
+ @Test
+ public void remainMethodNameConflictInHierarchy_useuniqueclassmembernames_overloadaggressively()
+ throws Exception {
+ Assume.assumeTrue(ToolHelper.artSupported());
+ JasminBuilder builder = buildMethodNameConflictInHierarchy();
+ ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+ assertEquals(0, javaOutput.exitCode);
+
+ List<String> pgConfigs = ImmutableList.of(
+ keepMainProguardConfiguration(CLASS_NAME),
+ "-useuniqueclassmembernames",
+ "-overloadaggressively", // no-op
+ "-dontshrink");
+ AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+ assertTrue(sup.isPresent());
+ MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(m1.isPresent());
+ assertTrue(m1.isRenamed());
+ MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(m2.isPresent());
+ assertTrue(m2.isRenamed());
+ assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+ ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+ assertTrue(sub.isPresent());
+ MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(subM1.isPresent());
+ assertTrue(subM1.isRenamed());
+ MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(subM2.isPresent());
+ assertTrue(subM2.isRenamed());
+ assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+ // No matter what, overloading methods should be renamed to the same name.
+ assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+ assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ assertEquals(0, artOutput.exitCode);
+ assertEquals(javaOutput.stdout, artOutput.stdout);
+ }
+
+
+ @Test
+ public void resolveMethodNameConflictInHierarchy_no_options() throws Exception {
+ Assume.assumeTrue(ToolHelper.artSupported());
+ JasminBuilder builder = buildMethodNameConflictInHierarchy();
+ ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+ assertEquals(0, javaOutput.exitCode);
+
+ List<String> pgConfigs = ImmutableList.of(
+ keepMainProguardConfiguration(CLASS_NAME),
+ "-dontshrink");
+ AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+ assertTrue(sup.isPresent());
+ MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(m1.isPresent());
+ assertTrue(m1.isRenamed());
+ MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(m2.isPresent());
+ assertTrue(m2.isRenamed());
+ // TODO(b/73149686): R8 should be able to fix this conflict w/o -overloadaggressively.
+ assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+ ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+ assertTrue(sub.isPresent());
+ MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(subM1.isPresent());
+ assertTrue(subM1.isRenamed());
+ MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(subM2.isPresent());
+ assertTrue(subM2.isRenamed());
+ // TODO(b/73149686): R8 should be able to fix this conflict w/o -overloadaggressively.
+ assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+ // No matter what, overloading methods should be renamed to the same name.
+ assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+ assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ assertEquals(0, artOutput.exitCode);
+ assertEquals(javaOutput.stdout, artOutput.stdout);
+ }
+
+ @Test
+ public void remainMethodNameConflictInHierarchy_overloadaggressively() throws Exception {
+ Assume.assumeTrue(ToolHelper.artSupported());
+ JasminBuilder builder = buildMethodNameConflictInHierarchy();
+ ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+ assertEquals(0, javaOutput.exitCode);
+
+ List<String> pgConfigs = ImmutableList.of(
+ keepMainProguardConfiguration(CLASS_NAME),
+ "-overloadaggressively",
+ "-dontshrink");
+ AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+ DexInspector dexInspector = new DexInspector(app);
+
+ ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+ assertTrue(sup.isPresent());
+ MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(m1.isPresent());
+ assertTrue(m1.isRenamed());
+ MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(m2.isPresent());
+ assertTrue(m2.isRenamed());
+ assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+ ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+ assertTrue(sub.isPresent());
+ MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+ assertTrue(subM1.isPresent());
+ assertTrue(subM1.isRenamed());
+ MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+ assertTrue(subM2.isPresent());
+ assertTrue(subM2.isRenamed());
+ assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+ // No matter what, overloading methods should be renamed to the same name.
+ assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+ assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ assertEquals(0, artOutput.exitCode);
+ assertEquals(javaOutput.stdout, artOutput.stdout);
+ }
}