Merge "Revert "Temporarily disable assertion that checks for builder in run_on_as_app""
diff --git a/build.gradle b/build.gradle
index 6afc31a..ad54e55 100644
--- a/build.gradle
+++ b/build.gradle
@@ -471,6 +471,8 @@
// Javac often runs out of stack space when compiling the tests.
// Increase the stack size for the javac process.
options.forkOptions.jvmArgs << "-Xss4m"
+ // Test compilation is sometimes hitting the default limit at 1g, increase it.
+ options.forkOptions.jvmArgs << "-Xmx2g"
// Set the bootclass path so compilation is consistent with 1.8 target compatibility.
options.forkOptions.jvmArgs << "-Xbootclasspath/a:third_party/openjdk/openjdk-rt-1.8/rt.jar"
}
@@ -1434,8 +1436,6 @@
// See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html.
maxParallelForks = Runtime.runtime.availableProcessors().intdiv(coresPerFork) ?: 1
forkEvery = 0
- // Use the Concurrent Mark Sweep GC (CMS) to keep memory usage at a resonable level.
- jvmArgs = ["-XX:+UseConcMarkSweepGC"]
if (project.hasProperty('disable_assertions')) {
enableAssertions = false
}
@@ -1508,7 +1508,7 @@
if (project.hasProperty('r8lib') || project.hasProperty('r8lib_no_deps')) {
def out = new StringBuffer()
def err = new StringBuffer()
- def command = "tools/retrace.py"
+ def command = "python tools/retrace.py"
def header = "RETRACED STACKTRACE";
if (System.getenv('BUILDBOT_BUILDERNAME') != null
&& !System.getenv('BUILDBOT_BUILDERNAME').endsWith("_release")) {
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 5131b19..6705f0a 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -85,6 +85,7 @@
builders {
name: "archive"
+ priority: 25
mixins: "linux"
execution_timeout_secs: 1800 # 1/2h
recipe {
@@ -93,6 +94,7 @@
}
builders {
name: "archive_release"
+ priority: 25
mixins: "linux"
execution_timeout_secs: 1800 # 1/2h
recipe {
diff --git a/infra/config/global/luci-notify.cfg b/infra/config/global/luci-notify.cfg
new file mode 100644
index 0000000..b8d8a32
--- /dev/null
+++ b/infra/config/global/luci-notify.cfg
@@ -0,0 +1,161 @@
+# Defines email notifications for builders.
+# See schema at
+# https://chromium.googlesource.com/infra/luci/luci-go/+/master/luci_notify/api/config/notify.proto
+#
+
+notifiers {
+ name: "r8-failures"
+ notifications {
+ on_change: false
+ on_success: false
+ on_failure: false
+ on_new_failure: true
+ email {
+ # Temporary, to ensure that it works.
+ recipients: "ricow@google.com"
+ }
+ # This means send to all people on the blamelist!
+ notify_blamelist {}
+ }
+ builders {
+ name: "archive"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "archive_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-4.0.4"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-4.0.4_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-4.4.4"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-4.4.4_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-5.1.1"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-5.1.1_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-6.0.1"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-6.0.1_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-7.0.0"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-android-7.0.0_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-jctf"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux-jctf_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "d8-linux_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-4.0.4"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-4.0.4_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-4.4.4"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-4.4.4_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-5.1.1"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-5.1.1_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-6.0.1"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-6.0.1_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-7.0.0"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-android-7.0.0_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-internal"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-internal_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-jctf"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "linux-jctf_release"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "r8cf-linux-jctf"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "windows"
+ bucket: "luci.r8.ci"
+ }
+ builders {
+ name: "windows_release"
+ bucket: "luci.r8.ci"
+ }
+}
+
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 2b67476..4de5097 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -74,6 +74,9 @@
job {
id: "archive"
acl_sets: "default"
+ triggering_policy: {
+ max_concurrent_invocations: 3
+ }
buildbucket {
server: "cr-buildbucket.appspot.com"
bucket: "luci.r8.ci"
@@ -403,6 +406,9 @@
job {
id: "windows"
+ triggering_policy: {
+ max_concurrent_invocations: 3
+ }
acl_sets: "default"
buildbucket {
server: "cr-buildbucket.appspot.com"
@@ -414,6 +420,9 @@
job {
id: "windows_release"
acl_sets: "default"
+ triggering_policy: {
+ max_concurrent_invocations: 3
+ }
buildbucket {
server: "cr-buildbucket.appspot.com"
bucket: "luci.r8.ci"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 95e3156..8069844 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -276,6 +276,7 @@
RootSet rootSet;
String proguardSeedsData = null;
timing.begin("Strip unused code");
+ Set<DexType> classesToRetainInnerClassAttributeFor = null;
try {
Set<DexType> missingClasses = appView.appInfo().getMissingClasses();
missingClasses = filterMissingClasses(
@@ -352,7 +353,14 @@
new AbstractMethodRemover(appView.appInfo().withLiveness()).run();
}
- new AnnotationRemover(appView.appInfo().withLiveness(), appView.graphLense(), options)
+ classesToRetainInnerClassAttributeFor =
+ AnnotationRemover.computeClassesToRetainInnerClassAttributeFor(
+ appView.appInfo().withLiveness(), options);
+ new AnnotationRemover(
+ appView.appInfo().withLiveness(),
+ appView.graphLense(),
+ options,
+ classesToRetainInnerClassAttributeFor)
.ensureValid(compatibility)
.run();
@@ -609,7 +617,12 @@
}
}
// Remove annotations that refer to types that no longer exist.
- new AnnotationRemover(appView.appInfo().withLiveness(), appView.graphLense(), options)
+ assert classesToRetainInnerClassAttributeFor != null;
+ new AnnotationRemover(
+ appView.appInfo().withLiveness(),
+ appView.graphLense(),
+ options,
+ classesToRetainInnerClassAttributeFor)
.run();
if (!mainDexClasses.isEmpty()) {
// Remove types that no longer exists from the computed main dex list.
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 3d9336e..90f01b2 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -684,13 +684,11 @@
internal.getProguardConfiguration().getKeepAttributes().lineNumberTable = true;
internal.getProguardConfiguration().getKeepAttributes().localVariableTable = true;
internal.getProguardConfiguration().getKeepAttributes().localVariableTypeTable = true;
- // TODO(zerny): Should we support inlining in debug mode? b/62937285
internal.enableInlining = false;
internal.enableClassInlining = false;
internal.enableHorizontalClassMerging = false;
internal.enableVerticalClassMerging = false;
internal.enableClassStaticizer = false;
- // TODO(zerny): Should we support outlining in debug mode? b/62937285
internal.outline.enabled = false;
}
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index b0cef90..f35021b 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.5.9-dev";
+ public static final String LABEL = "1.5.10-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 5be17c6..1f62c88 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -162,7 +162,7 @@
assert guard != null;
builder
.append(".catch ")
- .append(guard == DexItemFactory.catchAllType ? "all" : guard.getInternalName())
+ .append(guard.getInternalName()) // Do we wan't to write 'all' here?
.append(" from ")
.append(getLabel(tryCatch.start))
.append(" to ")
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index f6454f9..ad6c7d2 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -215,7 +215,9 @@
int computedMinApiLevel = options.minApiLevel;
for (ProgramResource input : dexSources) {
DexReader dexReader = new DexReader(input);
- computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
+ if (options.passthroughDexCode) {
+ computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
+ }
dexParsers.add(new DexParser(dexReader, classKind, itemFactory, options.reporter));
}
options.minApiLevel = computedMinApiLevel;
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 2042392..6abc3fd 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -174,7 +174,9 @@
start,
end,
target,
- guard == DexItemFactory.catchAllType ? null : namingLens.lookupInternalName(guard));
+ guard == options.itemFactory.throwableType
+ ? null
+ : namingLens.lookupInternalName(guard));
}
}
for (LocalVariableInfo localVariable : localVariables) {
@@ -280,9 +282,7 @@
}
for (CfTryCatch tryCatch : tryCatchRanges) {
for (DexType guard : tryCatch.guards) {
- if (guard != DexItemFactory.catchAllType) {
- registry.registerTypeReference(guard);
- }
+ registry.registerTypeReference(guard);
}
}
}
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 0e58ec0..035b472 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -49,6 +49,8 @@
public class DexItemFactory {
+ public static final String throwableDescriptorString = "Ljava/lang/Throwable;";
+
private final ConcurrentHashMap<DexString, DexString> strings = new ConcurrentHashMap<>();
private final ConcurrentHashMap<DexString, DexType> types = new ConcurrentHashMap<>();
private final ConcurrentHashMap<DexField, DexField> fields = new ConcurrentHashMap<>();
@@ -76,8 +78,6 @@
boolean sorted = false;
- public static final DexType catchAllType = new DexType(new DexString("CATCH_ALL"));
-
// Internal type containing only the null value.
public static final DexType nullValueType = new DexType(new DexString("NULL"));
@@ -86,7 +86,6 @@
private static final IdentityHashMap<DexItem, DexItem> internalSentinels =
new IdentityHashMap<>(
ImmutableMap.of(
- catchAllType, catchAllType,
nullValueType, nullValueType,
unknownTypeName, unknownTypeName));
@@ -178,7 +177,7 @@
public final DexString methodDescriptor = createString("Ljava/lang/reflect/Method;");
public final DexString enumDescriptor = createString("Ljava/lang/Enum;");
public final DexString annotationDescriptor = createString("Ljava/lang/annotation/Annotation;");
- public final DexString throwableDescriptor = createString("Ljava/lang/Throwable;");
+ public final DexString throwableDescriptor = createString(throwableDescriptorString);
public final DexString exceptionInInitializerErrorDescriptor =
createString("Ljava/lang/ExceptionInInitializerError;");
public final DexString objectsDescriptor = createString("Ljava/util/Objects;");
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index fa03827..a09ba4f 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -819,7 +819,7 @@
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
List<DexType> guards =
Collections.singletonList(
- type == null ? DexItemFactory.catchAllType : createTypeFromInternalType(type));
+ type == null ? factory.throwableType : createTypeFromInternalType(type));
List<CfLabel> targets = Collections.singletonList(getLabel(handler));
tryCatchRanges.add(new CfTryCatch(getLabel(start), getLabel(end), guards, targets));
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index a19d070..9532404 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -191,10 +191,6 @@
}
DexType guard = catchHandler.guard;
- if (guard == DexItemFactory.catchAllType) {
- return AnalysisAssumption.NONE;
- }
-
if (exceptionalExit.isInstanceGet()
|| exceptionalExit.isInstancePut()
|| exceptionalExit.isInvokeMethodWithReceiver()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 589b650..47013dd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -854,7 +854,8 @@
List<DexType> guards = catchHandlers.getGuards();
int lastGuardIndex = guards.size() - 1;
for (int i = 0; i < guards.size(); i++) {
- assert guards.get(i) != DexItemFactory.catchAllType || i == lastGuardIndex;
+ assert !guards.get(i).toDescriptorString().equals(DexItemFactory.throwableDescriptorString)
+ || i == lastGuardIndex;
}
// Check that all successors except maybe the last are catch successors.
List<Integer> sortedHandlerIndices = new ArrayList<>(catchHandlers.getAllTargets());
@@ -1533,7 +1534,7 @@
ListIterator<BasicBlock> blockIterator,
BasicBlock fromBlock,
InternalOptions options) {
- if (catchHandlers != null && catchHandlers.hasCatchAll()) {
+ if (catchHandlers != null && catchHandlers.hasCatchAll(options.itemFactory)) {
return;
}
List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock);
diff --git a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
index 34926fa..237c0cc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
@@ -71,9 +71,9 @@
return uniqueTargets;
}
- public boolean hasCatchAll() {
- return getGuards().size() > 0 &&
- getGuards().get(getGuards().size() - 1) == DexItemFactory.catchAllType;
+ public boolean hasCatchAll(DexItemFactory factory) {
+ return getGuards().size() > 0
+ && getGuards().get(getGuards().size() - 1) == factory.throwableType;
}
public CatchHandlers<T> removeGuard(DexType guardToBeRemoved) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 2c10fbd..661d3e9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -79,7 +79,8 @@
static TryHandlerList computeTryHandlers(
int instructionOffset,
List<CfTryCatch> tryCatchRanges,
- Reference2IntMap<CfLabel> labelOffsets) {
+ Reference2IntMap<CfLabel> labelOffsets,
+ DexItemFactory factory) {
int startOffset = Integer.MIN_VALUE;
int endOffset = Integer.MAX_VALUE;
List<DexType> guards = new ArrayList<>();
@@ -103,7 +104,7 @@
if (seen.add(guard)) {
guards.add(guard);
offsets.add(labelOffsets.getInt(tryCatch.targets.get(i)));
- seenCatchAll = guard == DexItemFactory.catchAllType;
+ seenCatchAll = guard == factory.throwableType;
}
}
if (seenCatchAll) {
@@ -266,7 +267,7 @@
CfInstruction instruction = code.getInstructions().get(instructionIndex);
assert builder.isGeneratingClassFiles() == internalOutputMode.isGeneratingClassFiles();
if (canThrowHelper(instruction)) {
- TryHandlerList tryHandlers = getTryHandlers(instructionIndex);
+ TryHandlerList tryHandlers = getTryHandlers(instructionIndex, builder.getFactory());
if (!tryHandlers.isEmpty()) {
// Ensure the block starts at the start of the try-range (don't enqueue, not a target).
builder.ensureBlockWithoutEnqueuing(tryHandlers.startOffset);
@@ -295,11 +296,11 @@
return -1;
}
- private TryHandlerList getTryHandlers(int instructionOffset) {
+ private TryHandlerList getTryHandlers(int instructionOffset, DexItemFactory factory) {
if (cachedTryHandlerList == null || !cachedTryHandlerList.validFor(instructionOffset)) {
cachedTryHandlerList =
TryHandlerList.computeTryHandlers(
- instructionOffset, code.getTryCatchRanges(), labelOffsets);
+ instructionOffset, code.getTryCatchRanges(), labelOffsets, factory);
}
return cachedTryHandlerList;
}
@@ -623,8 +624,9 @@
}
@Override
- public CatchHandlers<Integer> getCurrentCatchHandlers() {
- TryHandlerList tryHandlers = getTryHandlers(instructionOffset(currentInstructionIndex));
+ public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
+ TryHandlerList tryHandlers =
+ getTryHandlers(instructionOffset(currentInstructionIndex), builder.getFactory());
if (tryHandlers.isEmpty()) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index ec2fe4c..6fbe1e2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -42,7 +42,6 @@
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexDebugEventBuilder;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.Argument;
@@ -803,7 +802,7 @@
DexType type = handlerGroup.getGuards().get(i);
BasicBlock target = handlerGroup.getAllTargets().get(i);
int targetOffset = getInfo(target.entry()).getOffset();
- if (type == DexItemFactory.catchAllType) {
+ if (type == options.itemFactory.throwableType) {
assert i == handlerGroup.getGuards().size() - 1;
catchAllOffset = targetOffset;
} else {
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 4738403..0790018 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
@@ -53,6 +53,7 @@
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
public class DexSourceCode implements SourceCode {
@@ -202,14 +203,14 @@
@Override
public void buildInstruction(
IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
- updateCurrentCatchHandlers(instructionIndex);
+ updateCurrentCatchHandlers(instructionIndex, builder.getFactory());
updateDebugPosition(instructionIndex, builder);
currentDexInstruction = code.instructions[instructionIndex];
currentDexInstruction.buildIR(builder);
}
@Override
- public CatchHandlers<Integer> getCurrentCatchHandlers() {
+ public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
return currentCatchHandlers;
}
@@ -246,7 +247,7 @@
return true;
}
- private void updateCurrentCatchHandlers(int instructionIndex) {
+ private void updateCurrentCatchHandlers(int instructionIndex, DexItemFactory factory) {
Try tryRange = getTryForOffset(instructionOffset(instructionIndex));
if (tryRange == currentTryRange) {
return;
@@ -255,9 +256,7 @@
if (tryRange == null) {
currentCatchHandlers = null;
} else {
- currentCatchHandlers = new CatchHandlers<>(
- getTryHandlerGuards(tryRange),
- getTryHandlerOffsets(tryRange));
+ currentCatchHandlers = getCurrentCatchHandlers(factory, tryRange);
}
}
@@ -391,7 +390,7 @@
}
builder.ensureBlockWithoutEnqueuing(tryRangeStartAddress);
// Edge to exceptional successors.
- for (Integer handlerOffset : getUniqueTryHandlerOffsets(tryRange)) {
+ for (Integer handlerOffset : getUniqueTryHandlerOffsets(tryRange, builder.getFactory())) {
builder.ensureExceptionalSuccessorBlock(offset, handlerOffset);
}
// If the following instruction is a move-result include it in this (the invokes) block.
@@ -437,31 +436,46 @@
return null;
}
- private Set<Integer> getUniqueTryHandlerOffsets(Try tryRange) {
- return new HashSet<>(getTryHandlerOffsets(tryRange));
- }
-
- private List<Integer> getTryHandlerOffsets(Try tryRange) {
- List<Integer> handlerOffsets = new ArrayList<>();
- TryHandler handler = code.handlers[tryRange.handlerIndex];
- for (TypeAddrPair pair : handler.pairs) {
- handlerOffsets.add(pair.addr);
- }
- if (handler.catchAllAddr != TryHandler.NO_HANDLER) {
- handlerOffsets.add(handler.catchAllAddr);
- }
- return handlerOffsets;
- }
-
- private List<DexType> getTryHandlerGuards(Try tryRange) {
+ private CatchHandlers<Integer> getCurrentCatchHandlers(DexItemFactory factory, Try tryRange) {
List<DexType> handlerGuards = new ArrayList<>();
+ List<Integer> handlerOffsets = new ArrayList<>();
+ forEachTryRange(
+ tryRange,
+ factory,
+ (type, addr) -> {
+ handlerGuards.add(type);
+ handlerOffsets.add(addr);
+ });
+ return new CatchHandlers<>(handlerGuards, handlerOffsets);
+ }
+
+ private void forEachTryRange(
+ Try tryRange, DexItemFactory factory, BiConsumer<DexType, Integer> fn) {
TryHandler handler = code.handlers[tryRange.handlerIndex];
for (TypeAddrPair pair : handler.pairs) {
- handlerGuards.add(pair.type);
+ fn.accept(pair.type, pair.addr);
+ if (pair.type == factory.throwableType) {
+ return;
+ }
}
if (handler.catchAllAddr != TryHandler.NO_HANDLER) {
- handlerGuards.add(DexItemFactory.catchAllType);
+ fn.accept(factory.throwableType, handler.catchAllAddr);
}
- return handlerGuards;
+
+ }
+
+ private Set<Integer> getUniqueTryHandlerOffsets(Try tryRange, DexItemFactory factory) {
+ return new HashSet<>(getTryHandlerOffsets(tryRange, factory));
+ }
+
+ private List<Integer> getTryHandlerOffsets(Try tryRange, DexItemFactory factory) {
+ List<Integer> handlerOffsets = new ArrayList<>();
+ forEachTryRange(
+ tryRange,
+ factory,
+ (type, addr) -> {
+ handlerOffsets.add(addr);
+ });
+ return handlerOffsets;
}
}
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 0f09bbc..2e22480 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
@@ -2216,7 +2216,7 @@
offsets.put(block, freshOffset);
// Copy over the exceptional successors.
- for (int offset : source.getCurrentCatchHandlers().getUniqueTargets()) {
+ for (int offset : source.getCurrentCatchHandlers(this).getUniqueTargets()) {
info.addExceptionalSuccessor(offset);
BlockInfo target = targets.get(offset);
assert !target.block.isSealed();
@@ -2252,27 +2252,25 @@
currentBlock.add(ir);
if (ir.instructionTypeCanThrow()) {
assert source.verifyCurrentInstructionCanThrow();
- CatchHandlers<Integer> catchHandlers = source.getCurrentCatchHandlers();
+ CatchHandlers<Integer> catchHandlers = source.getCurrentCatchHandlers(this);
if (catchHandlers != null) {
assert !throwingInstructionInCurrentBlock;
throwingInstructionInCurrentBlock = true;
List<BasicBlock> targets = new ArrayList<>(catchHandlers.getAllTargets().size());
Set<BasicBlock> moveExceptionTargets = Sets.newIdentityHashSet();
- catchHandlers.forEach((type, targetOffset) -> {
- DexType exceptionType = type == options.itemFactory.catchAllType
- ? options.itemFactory.throwableType
- : type;
- BasicBlock header = new BasicBlock();
- header.incrementUnfilledPredecessorCount();
- ssaWorklist.add(
- new MoveExceptionWorklistItem(
- header, exceptionType, currentInstructionOffset, targetOffset));
- targets.add(header);
- BasicBlock target = getTarget(targetOffset);
- if (!moveExceptionTargets.add(target)) {
- target.incrementUnfilledPredecessorCount();
- }
- });
+ catchHandlers.forEach(
+ (exceptionType, targetOffset) -> {
+ BasicBlock header = new BasicBlock();
+ header.incrementUnfilledPredecessorCount();
+ ssaWorklist.add(
+ new MoveExceptionWorklistItem(
+ header, exceptionType, currentInstructionOffset, targetOffset));
+ targets.add(header);
+ BasicBlock target = getTarget(targetOffset);
+ if (!moveExceptionTargets.add(target)) {
+ target.incrementUnfilledPredecessorCount();
+ }
+ });
currentBlock.linkCatchSuccessors(catchHandlers.getGuards(), targets);
}
}
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 0c90322..9bfa951 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
@@ -563,7 +563,7 @@
if (inliner != null) {
printPhase("Double caller inlining");
assert graphLenseForIR == graphLense();
- inliner.processDoubleInlineCallers(this, feedback);
+ inliner.processDoubleInlineCallers(this, executorService, feedback);
feedback.updateVisibleOptimizationInfo();
assert graphLenseForIR == graphLense();
}
@@ -950,7 +950,6 @@
previous = printMethod(code, "IR after null tracking (SSA)", previous);
if (!isDebugMode && options.enableInlining && inliner != null) {
- // TODO(zerny): Should we support inlining in debug mode? b/62937285
inliner.performInlining(method, code, isProcessedConcurrently, callSiteInformation);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 93e5355..6574551 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProto;
@@ -96,7 +95,11 @@
this.handler = handler;
this.start = start;
this.end = end;
- this.type = type;
+ this.type = type == null ? "java/lang/Throwable" : type;
+ }
+
+ boolean isCatchAll() {
+ return type.equals("java/lang/Throwable");
}
int getStart() {
@@ -617,7 +620,7 @@
}
@Override
- public CatchHandlers<Integer> getCurrentCatchHandlers() {
+ public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
if (generatingMethodSynchronization) {
return null;
}
@@ -811,7 +814,7 @@
Set<String> seen = new HashSet<>();
// The try-catch blocks are ordered by precedence.
for (TryCatchBlock tryCatchBlock : getPotentialTryHandlers(insn)) {
- if (tryCatchBlock.getType() == null) {
+ if (tryCatchBlock.isCatchAll()) {
handlers.add(tryCatchBlock);
return handlers;
}
@@ -839,10 +842,10 @@
private List<DexType> getTryHandlerGuards(List<TryCatchBlock> tryCatchBlocks) {
List<DexType> guards = new ArrayList<>();
for (TryCatchBlock tryCatchBlock : tryCatchBlocks) {
- guards.add(tryCatchBlock.getType() == null
- ? DexItemFactory.catchAllType
- : application.getTypeFromName(tryCatchBlock.getType()));
-
+ guards.add(
+ tryCatchBlock.getType() == null
+ ? application.getFactory().throwableType
+ : application.getTypeFromName(tryCatchBlock.getType()));
}
return guards;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index fd968d6..d634827 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY;
import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
+import static com.android.tools.r8.ir.code.Invoke.Type.VIRTUAL;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -149,7 +150,31 @@
InvokeMethod invoke = current.asInvokeMethod();
DexMethod invokedMethod = invoke.getInvokedMethod();
DexType invokedHolder = invokedMethod.getHolder();
+ if (invokedHolder.isArrayType()) {
+ if (invokedMethod.name == appInfo.dexItemFactory.cloneMethodName) {
+ DexType baseType = invokedHolder.toBaseType(appInfo.dexItemFactory);
+ DexType mappedBaseType = graphLense.lookupType(baseType);
+ if (baseType != mappedBaseType) {
+ DexType mappedHolder =
+ invokedHolder.replaceBaseType(mappedBaseType, appInfo.dexItemFactory);
+ // The clone proto is ()Ljava/lang/Object;, so just reuse it.
+ DexMethod actualTarget =
+ appInfo.dexItemFactory.createMethod(
+ mappedHolder, invokedMethod.proto, appInfo.dexItemFactory.cloneMethodName);
+ Invoke newInvoke =
+ Invoke.create(
+ VIRTUAL,
+ actualTarget,
+ null,
+ makeOutValue(invoke, code, newSSAValues),
+ invoke.inValues());
+ iterator.replaceCurrentInstruction(newInvoke);
+ }
+ }
+ continue;
+ }
if (!invokedHolder.isClassType()) {
+ assert false;
continue;
}
if (invoke.isInvokeDirect()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
index 9c0dccb..df2b663 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
@@ -63,7 +63,7 @@
// Helper to resolve fill-array data and build new-array instructions (dex code only).
void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset, IRBuilder builder);
- CatchHandlers<Integer> getCurrentCatchHandlers();
+ CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder);
int getMoveExceptionRegister(int instructionIndex);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 504d01c..3d45805 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -180,9 +179,6 @@
for (int i = 0; i < catchHandlers.size(); ++i) {
DexType guard = catchHandlers.getGuards().get(i);
BasicBlock target = catchHandlers.getAllTargets().get(i);
- if (guard == DexItemFactory.catchAllType) {
- continue;
- }
// We can exploit subtyping information to eliminate a catch handler if the guard is
// subsumed by a previous guard.
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 953562f..aa42b7b 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
@@ -33,6 +33,7 @@
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashMap;
@@ -40,8 +41,10 @@
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
public class Inliner {
@@ -161,24 +164,29 @@
return target;
}
- public synchronized void processDoubleInlineCallers(
- IRConverter converter, OptimizationFeedback feedback) {
- if (doubleInlineCallers.size() > 0) {
- applyDoubleInlining = true;
- List<DexEncodedMethod> methods = doubleInlineCallers
- .stream()
- .sorted(DexEncodedMethod::slowCompare)
- .collect(Collectors.toList());
- for (DexEncodedMethod method : methods) {
- converter.processMethod(
- method,
- feedback,
- x -> false,
- CallSiteInformation.empty(),
- Outliner::noProcessing);
- assert method.isProcessed();
- }
+ public void processDoubleInlineCallers(
+ IRConverter converter, ExecutorService executorService, OptimizationFeedback feedback)
+ throws ExecutionException {
+ if (doubleInlineCallers.isEmpty()) {
+ return;
}
+ applyDoubleInlining = true;
+ List<Future<?>> futures = new ArrayList<>();
+ for (DexEncodedMethod method : doubleInlineCallers) {
+ futures.add(
+ executorService.submit(
+ () -> {
+ converter.processMethod(
+ method,
+ feedback,
+ doubleInlineCallers::contains,
+ CallSiteInformation.empty(),
+ Outliner::noProcessing);
+ assert method.isProcessed();
+ return null;
+ }));
+ }
+ ThreadUtils.awaitFutures(futures);
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 27ec7dc..5ba7eb1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1453,7 +1453,7 @@
}
@Override
- public CatchHandlers<Integer> getCurrentCatchHandlers() {
+ public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 9dff882..ff330e1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -581,8 +581,7 @@
// target.
return;
}
- if (guard != dexItemFactory.catchAllType
- && !dexItemFactory.npeType.isSubtypeOf(guard, appView.appInfo())) {
+ if (!dexItemFactory.npeType.isSubtypeOf(guard, appView.appInfo())) {
// TODO(christofferqa): Consider updating previous dominator tree instead of
// rebuilding it from scratch.
DominatorTree dominatorTree = new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index ab6f635..83998e4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -37,7 +37,6 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
-import com.google.common.base.Predicates;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.IdentityHashMap;
@@ -241,7 +240,7 @@
// Rewrite lambda class references into lambda group class
// references inside methods from the processing queue.
- rewriteLambdaReferences(converter, feedback);
+ rewriteLambdaReferences(converter, executorService, feedback);
this.strategyFactory = null;
}
@@ -316,19 +315,32 @@
}
}
- private void rewriteLambdaReferences(IRConverter converter, OptimizationFeedback feedback) {
- List<DexEncodedMethod> methods =
- methodsToReprocess
- .stream()
- .sorted(DexEncodedMethod::slowCompare)
- .collect(Collectors.toList());
- for (DexEncodedMethod method : methods) {
- DexEncodedMethod mappedMethod =
- converter.graphLense().mapDexEncodedMethod(method, converter.appInfo);
- converter.processMethod(mappedMethod, feedback,
- Predicates.alwaysFalse(), CallSiteInformation.empty(), Outliner::noProcessing);
- assert mappedMethod.isProcessed();
+ private void rewriteLambdaReferences(
+ IRConverter converter, ExecutorService executorService, OptimizationFeedback feedback)
+ throws ExecutionException {
+ if (methodsToReprocess.isEmpty()) {
+ return;
}
+ Set<DexEncodedMethod> methods =
+ methodsToReprocess.stream()
+ .map(method -> converter.graphLense().mapDexEncodedMethod(method, converter.appInfo))
+ .collect(Collectors.toSet());
+ List<Future<?>> futures = new ArrayList<>();
+ for (DexEncodedMethod method : methods) {
+ futures.add(
+ executorService.submit(
+ () -> {
+ converter.processMethod(
+ method,
+ feedback,
+ methods::contains,
+ CallSiteInformation.empty(),
+ Outliner::noProcessing);
+ assert method.isProcessed();
+ return null;
+ }));
+ }
+ ThreadUtils.awaitFutures(futures);
}
private void analyzeClass(DexProgramClass clazz) {
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 50d63e5..031ae7e 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
@@ -238,7 +238,7 @@
}
@Override
- public final CatchHandlers<Integer> getCurrentCatchHandlers() {
+ public final CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index d7426fc..92c07d1 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
@@ -25,8 +24,6 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
@@ -44,7 +41,9 @@
private final AppView<AppInfoWithLiveness> appView;
private final AppInfoWithLiveness appInfo;
- private final Reporter reporter;
+ private final ClassNamingStrategy classNamingStrategy;
+ private final PackageNamingStrategy packageNamingStrategy;
+ private final Iterable<? extends DexClass> classes;
private final PackageObfuscationMode packageObfuscationMode;
private final boolean isAccessModificationAllowed;
private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
@@ -62,11 +61,18 @@
private final Namespace topLevelState;
- ClassNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+ ClassNameMinifier(
+ AppView<AppInfoWithLiveness> appView,
+ RootSet rootSet,
+ ClassNamingStrategy classNamingStrategy,
+ PackageNamingStrategy packageNamingStrategy,
+ Iterable<? extends DexClass> classes) {
this.appView = appView;
this.appInfo = appView.appInfo();
+ this.classNamingStrategy = classNamingStrategy;
+ this.packageNamingStrategy = packageNamingStrategy;
+ this.classes = classes;
InternalOptions options = appView.options();
- this.reporter = options.reporter;
this.packageObfuscationMode = options.getProguardConfiguration().getPackageObfuscationMode();
this.isAccessModificationAllowed =
options.getProguardConfiguration().isAccessModificationAllowed();
@@ -99,8 +105,6 @@
}
ClassRenaming computeRenaming(Timing timing) {
- // Use deterministic class order to make sure renaming is deterministic.
- Iterable<DexProgramClass> classes = appInfo.classesWithDeterministicOrder();
// Collect names we have to keep.
timing.begin("reserve");
for (DexClass clazz : classes) {
@@ -175,7 +179,7 @@
// return type. As we don't need the class, we can rename it to anything as long as it is
// unique.
assert appInfo.definitionFor(type) == null;
- renaming.put(type, topLevelState.nextTypeName());
+ renaming.put(type, topLevelState.nextTypeName(type));
}
}
@@ -250,7 +254,7 @@
if (state == null) {
state = getStateForClass(type);
}
- return state.nextTypeName();
+ return state.nextTypeName(type);
}
private Namespace getStateForClass(DexType type) {
@@ -344,12 +348,10 @@
}
}
- private class Namespace {
+ protected class Namespace {
private final String packageName;
private final char[] packagePrefix;
- private int typeCounter = 1;
- private int packageCounter = 1;
private final Iterator<String> packageDictionaryIterator;
private final Iterator<String> classDictionaryIterator;
@@ -376,37 +378,37 @@
return packageName;
}
- private String nextSuggestedNameForClass() {
+ private DexString nextSuggestedNameForClass(DexType type) {
StringBuilder nextName = new StringBuilder();
- if (classDictionaryIterator.hasNext()) {
+ if (!classNamingStrategy.bypassDictionary() && classDictionaryIterator.hasNext()) {
nextName.append(packagePrefix).append(classDictionaryIterator.next()).append(';');
- return nextName.toString();
+ return appInfo.dexItemFactory.createString(nextName.toString());
} else {
- return StringUtils.numberToIdentifier(packagePrefix, typeCounter++, true);
+ return classNamingStrategy.next(this, type, packagePrefix);
}
}
- DexString nextTypeName() {
+ DexString nextTypeName(DexType type) {
DexString candidate;
do {
- candidate = appInfo.dexItemFactory.createString(nextSuggestedNameForClass());
+ candidate = nextSuggestedNameForClass(type);
} while (usedTypeNames.contains(candidate));
usedTypeNames.add(candidate);
return candidate;
}
private String nextSuggestedNameForSubpackage() {
- StringBuilder nextName = new StringBuilder();
// Note that the differences between this method and the other variant for class renaming are
// 1) this one uses the different dictionary and counter,
// 2) this one does not append ';' at the end, and
// 3) this one removes 'L' at the beginning to make the return value a binary form.
- if (packageDictionaryIterator.hasNext()) {
+ if (!packageNamingStrategy.bypassDictionary() && packageDictionaryIterator.hasNext()) {
+ StringBuilder nextName = new StringBuilder();
nextName.append(packagePrefix).append(packageDictionaryIterator.next());
+ return nextName.toString().substring(1);
} else {
- nextName.append(StringUtils.numberToIdentifier(packagePrefix, packageCounter++, false));
+ return packageNamingStrategy.next(this, packagePrefix);
}
- return nextName.toString().substring(1);
}
String nextPackagePrefix() {
@@ -419,6 +421,20 @@
}
}
+ protected interface ClassNamingStrategy {
+
+ DexString next(Namespace namespace, DexType type, char[] packagePrefix);
+
+ boolean bypassDictionary();
+ }
+
+ protected interface PackageNamingStrategy {
+
+ String next(Namespace namespace, char[] packagePrefix);
+
+ boolean bypassDictionary();
+ }
+
/**
* Compute parent package prefix from the given package prefix.
*
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 e2127dd..e419d5a 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -19,8 +19,9 @@
class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
- FieldNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
- super(appView, rootSet);
+ FieldNameMinifier(
+ AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
+ super(appView, rootSet, strategy);
}
@Override
@@ -106,7 +107,8 @@
private void renameField(DexEncodedField encodedField, NamingState<DexType, ?> state) {
DexField field = encodedField.field;
if (!state.isReserved(field.name, field.type)) {
- renaming.put(field, state.assignNewNameFor(field.name, field.type, useUniqueMemberNames));
+ renaming.put(
+ field, state.assignNewNameFor(field, field.name, field.type, useUniqueMemberNames));
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index b643be3..6c46119 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -234,7 +234,8 @@
sourceMethods.addAll(sourceMethodsMap.get(k));
for (NamingState<DexProto, ?> namingState : globalStateMap.get(k)) {
collectedStates.add(
- new MethodNamingState(namingState, unifiedMethod.name, unifiedMethod.proto));
+ new MethodNamingState(
+ namingState, unifiedMethod, unifiedMethod.name, unifiedMethod.proto));
}
}
@@ -249,7 +250,7 @@
}
MethodNamingState originState =
- new MethodNamingState(originStates.get(key), method.name, method.proto);
+ new MethodNamingState(originStates.get(key), method, method.name, method.proto);
assignNameForInterfaceMethodInAllStates(collectedStates, sourceMethods, originState);
}
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 53957e4..d60de88 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -5,8 +5,10 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CachedHashValueDexItem;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingState.InternalState;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
@@ -36,7 +38,8 @@
// which is useful for debugging.
private final BiMap<DexType, NamingState<StateType, ?>> states = HashBiMap.create();
- MemberNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+ MemberNameMinifier(
+ AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
this.appView = appView;
this.appInfo = appView.appInfo();
this.rootSet = rootSet;
@@ -45,8 +48,9 @@
this.useUniqueMemberNames = options.getProguardConfiguration().isUseUniqueClassMemberNames();
this.overloadAggressively =
options.getProguardConfiguration().isOverloadAggressivelyWithoutUseUniqueClassMemberNames();
- this.globalState = NamingState.createRoot(
- appInfo.dexItemFactory, dictionary, getKeyTransform(), useUniqueMemberNames);
+ this.globalState =
+ NamingState.createRoot(
+ appInfo.dexItemFactory, dictionary, getKeyTransform(), strategy, useUniqueMemberNames);
}
abstract Function<StateType, ?> getKeyTransform();
@@ -88,4 +92,10 @@
return useUniqueMemberNames;
}
}
+
+ interface MemberNamingStrategy {
+ DexString next(DexReference source, InternalState internalState);
+
+ boolean bypassDictionary();
+ }
}
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 2cd8712..e97910f 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -91,9 +91,12 @@
private final Equivalence<DexMethod> equivalence;
private final FrontierState frontierState = new FrontierState();
+ private final MemberNamingStrategy strategy;
- MethodNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
- super(appView, rootSet);
+ MethodNameMinifier(
+ AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
+ super(appView, rootSet, strategy);
+ this.strategy = strategy;
equivalence =
overloadAggressively
? MethodSignatureEquivalence.get()
@@ -188,7 +191,8 @@
DexString renamedName =
renamingAtThisLevel.computeIfAbsent(
equivalence.wrap(method),
- key -> state.assignNewNameFor(method.name, method.proto, useUniqueMemberNames));
+ key ->
+ state.assignNewNameFor(method, method.name, method.proto, useUniqueMemberNames));
renaming.put(method, renamedName);
}
}
@@ -233,6 +237,7 @@
appInfo.dexItemFactory,
dictionary,
getKeyTransform(),
+ strategy,
useUniqueMemberNames)
: parent.createChild());
@@ -276,8 +281,11 @@
private final NamingState<DexProto, ?> parent;
private final DexString name;
private final DexProto proto;
+ private final DexMethod method;
- MethodNamingState(NamingState<DexProto, ?> parent, DexString name, DexProto proto) {
+ MethodNamingState(
+ NamingState<DexProto, ?> parent, DexMethod method, DexString name, DexProto proto) {
+ this.method = method;
assert parent != null;
this.parent = parent;
this.name = name;
@@ -285,11 +293,7 @@
}
DexString assignNewName() {
- return parent.assignNewNameFor(name, proto, false);
- }
-
- void reserveName() {
- parent.reserveName(name, proto);
+ return parent.assignNewNameFor(method, name, proto, false);
}
boolean isReserved() {
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
new file mode 100644
index 0000000..f72efe1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
+import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
+import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
+import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+class MinifiedRenaming extends NamingLens {
+
+ private final AppInfo appInfo;
+ private final Map<String, String> packageRenaming;
+ private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
+
+ MinifiedRenaming(
+ ClassRenaming classRenaming,
+ MethodRenaming methodRenaming,
+ FieldRenaming fieldRenaming,
+ AppInfo appInfo) {
+ this.appInfo = appInfo;
+ this.packageRenaming = classRenaming.packageRenaming;
+ renaming.putAll(classRenaming.classRenaming);
+ renaming.putAll(methodRenaming.renaming);
+ renaming.putAll(methodRenaming.callSiteRenaming);
+ renaming.putAll(fieldRenaming.renaming);
+ }
+
+ @Override
+ public String lookupPackageName(String packageName) {
+ return packageRenaming.getOrDefault(packageName, packageName);
+ }
+
+ @Override
+ public DexString lookupDescriptor(DexType type) {
+ return renaming.getOrDefault(type, type.descriptor);
+ }
+
+ @Override
+ public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
+ if (attribute.getInnerName() == null) {
+ return null;
+ }
+ // The Java reflection library assumes that that inner-class names are separated by a $ and
+ // thus we allow the mapping of an inner name to rely on that too. If the dollar is not
+ // present after pulling off the original inner-name, then we revert to using the simple name
+ // of the inner class as its name.
+ DexType innerType = attribute.getInner();
+ String inner = DescriptorUtils.descriptorToInternalName(innerType.descriptor.toString());
+ String innerName = attribute.getInnerName().toString();
+ int lengthOfPrefix = inner.length() - innerName.length();
+ if (lengthOfPrefix < 0
+ || inner.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR, lengthOfPrefix - 1) < 0
+ || !inner.endsWith(innerName)) {
+ return lookupSimpleName(innerType, options.itemFactory);
+ }
+
+ // At this point we assume the input was of the form: <OuterType>$<index><InnerName>
+ // Find the mapped type and if it remains the same return that, otherwise split at $.
+ String innerTypeMapped =
+ DescriptorUtils.descriptorToInternalName(lookupDescriptor(innerType).toString());
+ if (inner.equals(innerTypeMapped)) {
+ return attribute.getInnerName();
+ }
+ int index = innerTypeMapped.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
+ if (index < 0) {
+ // TODO(b/120639028): Replace this by "assert false" and remove the testing option.
+ // Hitting means we have converted a proper Outer$Inner relationship to an invalid one.
+ assert !options.testing.allowFailureOnInnerClassErrors
+ : "Outer$Inner class was remapped without keeping the dollar separator";
+ return lookupSimpleName(innerType, options.itemFactory);
+ }
+ return options.itemFactory.createString(innerTypeMapped.substring(index + 1));
+ }
+
+ @Override
+ public DexString lookupName(DexMethod method) {
+ return renaming.getOrDefault(method, method.name);
+ }
+
+ @Override
+ public DexString lookupMethodName(DexCallSite callSite) {
+ return renaming.getOrDefault(callSite, callSite.methodName);
+ }
+
+ @Override
+ public DexString lookupName(DexField field) {
+ return renaming.getOrDefault(field, field.name);
+ }
+
+ @Override
+ void forAllRenamedTypes(Consumer<DexType> consumer) {
+ DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
+ .forEach(consumer);
+ }
+
+ @Override
+ <T extends DexItem> Map<String, T> getRenamedItems(
+ Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
+ return renaming.keySet().stream()
+ .filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
+ .map(clazz::cast)
+ .collect(ImmutableMap.toImmutableMap(namer, i -> i));
+ }
+
+ /**
+ * Checks whether the target is precise enough to be translated,
+ *
+ * <p>We only track the renaming of actual definitions, Thus, if we encounter a method id that
+ * does not directly point at a definition, we won't find the actual renaming. To avoid
+ * dispatching on every lookup, we assume that the tree has been fully dispatched by {@link
+ * MemberRebindingAnalysis}.
+ *
+ * <p>Library methods are excluded from this check, as those are never renamed.
+ */
+ @Override
+ public boolean checkTargetCanBeTranslated(DexMethod item) {
+ if (item.holder.isArrayType()) {
+ // Array methods are never renamed, so do not bother to check.
+ return true;
+ }
+ DexClass holder = appInfo.definitionFor(item.holder);
+ if (holder == null || holder.isLibraryClass()) {
+ return true;
+ }
+ // We don't know which invoke type this method is used for, so checks that it has been
+ // rebound either way.
+ DexEncodedMethod staticTarget = appInfo.lookupStaticTarget(item);
+ DexEncodedMethod directTarget = appInfo.lookupDirectTarget(item);
+ DexEncodedMethod virtualTarget = appInfo.lookupVirtualTarget(item.holder, item);
+ DexClass staticTargetHolder =
+ staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null;
+ DexClass directTargetHolder =
+ directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null;
+ DexClass virtualTargetHolder =
+ virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null;
+ return (directTarget == null && staticTarget == null && virtualTarget == null)
+ || (virtualTarget != null && virtualTarget.method == item)
+ || (directTarget != null && directTarget.method == item)
+ || (staticTarget != null && staticTarget.method == item)
+ || (directTargetHolder != null && directTargetHolder.isLibraryClass())
+ || (virtualTargetHolder != null && virtualTargetHolder.isLibraryClass())
+ || (staticTargetHolder != null && staticTargetHolder.isLibraryClass());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ renaming.forEach(
+ (item, str) -> {
+ if (item instanceof DexType) {
+ builder.append("[c] ");
+ } else if (item instanceof DexMethod) {
+ builder.append("[m] ");
+ } else if (item instanceof DexField) {
+ builder.append("[f] ");
+ }
+ builder.append(item.toSourceString());
+ builder.append(" -> ");
+ builder.append(str.toSourceString());
+ builder.append('\n');
+ });
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index be4fd39..9aa8502 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -3,34 +3,28 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
-import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItem;
-import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.ClassNameMinifier.ClassNamingStrategy;
import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
+import com.android.tools.r8.naming.ClassNameMinifier.Namespace;
+import com.android.tools.r8.naming.ClassNameMinifier.PackageNamingStrategy;
import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
+import com.android.tools.r8.naming.MemberNameMinifier.MemberNamingStrategy;
import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
-import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.naming.NamingState.InternalState;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableMap;
-import java.util.IdentityHashMap;
-import java.util.Map;
+import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.Set;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
public class Minifier {
@@ -54,7 +48,14 @@
public NamingLens run(Timing timing) {
assert options.enableMinification;
timing.begin("MinifyClasses");
- ClassNameMinifier classNameMinifier = new ClassNameMinifier(appView, rootSet);
+ ClassNameMinifier classNameMinifier =
+ new ClassNameMinifier(
+ appView,
+ rootSet,
+ new MinificationClassNamingStrategy(appInfo.dexItemFactory),
+ new MinificationPackageNamingStrategy(),
+ // Use deterministic class order to make sure renaming is deterministic.
+ appInfo.classesWithDeterministicOrder());
ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
timing.end();
@@ -62,16 +63,19 @@
classRenaming, MethodRenaming.empty(), FieldRenaming.empty(), appInfo)
.verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
+ MemberNamingStrategy minifyMembers = new MinifierMemberNamingStrategy(appView.dexItemFactory());
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
- new MethodNameMinifier(appView, rootSet).computeRenaming(desugaredCallSites, timing);
+ new MethodNameMinifier(appView, rootSet, minifyMembers)
+ .computeRenaming(desugaredCallSites, timing);
timing.end();
assert new MinifiedRenaming(classRenaming, methodRenaming, FieldRenaming.empty(), appInfo)
.verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
timing.begin("MinifyFields");
- FieldRenaming fieldRenaming = new FieldNameMinifier(appView, rootSet).computeRenaming(timing);
+ FieldRenaming fieldRenaming =
+ new FieldNameMinifier(appView, rootSet, minifyMembers).computeRenaming(timing);
timing.end();
NamingLens lens = new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
@@ -84,159 +88,73 @@
return lens;
}
- private static class MinifiedRenaming extends NamingLens {
+ static class MinificationClassNamingStrategy implements ClassNamingStrategy {
- private final AppInfo appInfo;
- private final Map<String, String> packageRenaming;
- private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
+ private final DexItemFactory factory;
+ private final Object2IntMap<Namespace> namespaceCounters = new Object2IntLinkedOpenHashMap<>();
- private MinifiedRenaming(
- ClassRenaming classRenaming,
- MethodRenaming methodRenaming,
- FieldRenaming fieldRenaming,
- AppInfo appInfo) {
- this.appInfo = appInfo;
- this.packageRenaming = classRenaming.packageRenaming;
- renaming.putAll(classRenaming.classRenaming);
- renaming.putAll(methodRenaming.renaming);
- renaming.putAll(methodRenaming.callSiteRenaming);
- renaming.putAll(fieldRenaming.renaming);
+ MinificationClassNamingStrategy(DexItemFactory factory) {
+ this.factory = factory;
+ namespaceCounters.defaultReturnValue(1);
}
@Override
- public String lookupPackageName(String packageName) {
- return packageRenaming.getOrDefault(packageName, packageName);
+ public DexString next(Namespace namespace, DexType type, char[] packagePrefix) {
+ int counter = namespaceCounters.put(namespace, namespaceCounters.getInt(namespace) + 1);
+ DexString string =
+ factory.createString(StringUtils.numberToIdentifier(packagePrefix, counter, true));
+ return string;
}
@Override
- public DexString lookupDescriptor(DexType type) {
- return renaming.getOrDefault(type, type.descriptor);
+ public boolean bypassDictionary() {
+ return false;
+ }
+ }
+
+ static class MinificationPackageNamingStrategy implements PackageNamingStrategy {
+
+ private final Object2IntMap<Namespace> namespaceCounters = new Object2IntLinkedOpenHashMap<>();
+
+ public MinificationPackageNamingStrategy() {
+ namespaceCounters.defaultReturnValue(1);
}
@Override
- public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
- if (attribute.getInnerName() == null) {
- return null;
- }
- // The Java reflection library assumes that that inner-class names are separated by a $ and
- // thus we allow the mapping of an inner name to rely on that too. If the dollar is not
- // present after pulling off the original inner-name, then we revert to using the simple name
- // of the inner class as its name.
- DexType innerType = attribute.getInner();
- String inner = DescriptorUtils.descriptorToInternalName(innerType.descriptor.toString());
- String innerName = attribute.getInnerName().toString();
- int lengthOfPrefix = inner.length() - innerName.length();
- if (lengthOfPrefix < 0
- || inner.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR, lengthOfPrefix - 1) < 0
- || !inner.endsWith(innerName)) {
- return lookupSimpleName(innerType, options.itemFactory);
- }
-
- // At this point we assume the input was of the form: <OuterType>$<index><InnerName>
- // Find the mapped type and if it remains the same return that, otherwise split at $.
- String innerTypeMapped =
- DescriptorUtils.descriptorToInternalName(lookupDescriptor(innerType).toString());
- if (inner.equals(innerTypeMapped)) {
- return attribute.getInnerName();
- }
- int index = innerTypeMapped.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
- if (index < 0) {
- // TODO(b/120639028): Replace this by "assert false" and remove the testing option.
- // Hitting means we have converted a proper Outer$Inner relationship to an invalid one.
- assert !options.testing.allowFailureOnInnerClassErrors
- : "Outer$Inner class was remapped without keeping the dollar separator";
- return lookupSimpleName(innerType, options.itemFactory);
- }
- return options.itemFactory.createString(innerTypeMapped.substring(index + 1));
+ public String next(Namespace namespace, char[] packagePrefix) {
+ // Note that the differences between this method and the other variant for class renaming are
+ // 1) this one uses the different dictionary and counter,
+ // 2) this one does not append ';' at the end, and
+ // 3) this one removes 'L' at the beginning to make the return value a binary form.
+ int counter = namespaceCounters.put(namespace, namespaceCounters.getInt(namespace) + 1);
+ return StringUtils.numberToIdentifier(packagePrefix, counter, false).substring(1);
}
@Override
- public DexString lookupName(DexMethod method) {
- return renaming.getOrDefault(method, method.name);
+ public boolean bypassDictionary() {
+ return false;
+ }
+ }
+
+ static class MinifierMemberNamingStrategy implements MemberNamingStrategy {
+
+ char[] EMPTY_CHAR_ARRAY = new char[0];
+
+ private final DexItemFactory factory;
+
+ public MinifierMemberNamingStrategy(DexItemFactory factory) {
+ this.factory = factory;
}
@Override
- public DexString lookupMethodName(DexCallSite callSite) {
- return renaming.getOrDefault(callSite, callSite.methodName);
+ public DexString next(DexReference dexReference, InternalState internalState) {
+ int counter = internalState.incrementAndGet();
+ return factory.createString(StringUtils.numberToIdentifier(EMPTY_CHAR_ARRAY, counter, false));
}
@Override
- public DexString lookupName(DexField field) {
- return renaming.getOrDefault(field, field.name);
- }
-
- @Override
- void forAllRenamedTypes(Consumer<DexType> consumer) {
- DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
- .forEach(consumer);
- }
-
- @Override
- <T extends DexItem> Map<String, T> getRenamedItems(
- Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
- return renaming.keySet().stream()
- .filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
- .map(clazz::cast)
- .collect(ImmutableMap.toImmutableMap(namer, i -> i));
- }
-
- /**
- * Checks whether the target is precise enough to be translated,
- * <p>
- * We only track the renaming of actual definitions, Thus, if we encounter a method id that
- * does not directly point at a definition, we won't find the actual renaming. To avoid
- * dispatching on every lookup, we assume that the tree has been fully dispatched by
- * {@link MemberRebindingAnalysis}.
- * <p>
- * Library methods are excluded from this check, as those are never renamed.
- */
- @Override
- public boolean checkTargetCanBeTranslated(DexMethod item) {
- if (item.holder.isArrayType()) {
- // Array methods are never renamed, so do not bother to check.
- return true;
- }
- DexClass holder = appInfo.definitionFor(item.holder);
- if (holder == null || holder.isLibraryClass()) {
- return true;
- }
- // We don't know which invoke type this method is used for, so checks that it has been
- // rebound either way.
- DexEncodedMethod staticTarget = appInfo.lookupStaticTarget(item);
- DexEncodedMethod directTarget = appInfo.lookupDirectTarget(item);
- DexEncodedMethod virtualTarget = appInfo.lookupVirtualTarget(item.holder, item);
- DexClass staticTargetHolder =
- staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null;
- DexClass directTargetHolder =
- directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null;
- DexClass virtualTargetHolder =
- virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null;
- return (directTarget == null && staticTarget == null && virtualTarget == null)
- || (virtualTarget != null && virtualTarget.method == item)
- || (directTarget != null && directTarget.method == item)
- || (staticTarget != null && staticTarget.method == item)
- || (directTargetHolder != null && directTargetHolder.isLibraryClass())
- || (virtualTargetHolder != null && virtualTargetHolder.isLibraryClass())
- || (staticTargetHolder != null && staticTargetHolder.isLibraryClass());
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- renaming.forEach((item, str) -> {
- if (item instanceof DexType) {
- builder.append("[c] ");
- } else if (item instanceof DexMethod) {
- builder.append("[m] ");
- } else if (item instanceof DexField) {
- builder.append("[f] ");
- }
- builder.append(item.toSourceString());
- builder.append(" -> ");
- builder.append(str.toSourceString());
- builder.append('\n');
- });
- return builder.toString();
+ public boolean bypassDictionary() {
+ return false;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
index d414b3b..a3352ad 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -5,8 +5,9 @@
import com.android.tools.r8.graph.CachedHashValueDexItem;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.naming.MemberNameMinifier.MemberNamingStrategy;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -27,14 +28,17 @@
private final DexItemFactory itemFactory;
private final List<String> dictionary;
private final Function<ProtoType, KeyType> keyTransform;
+ private final MemberNamingStrategy strategy;
private final boolean useUniqueMemberNames;
static <S, T extends CachedHashValueDexItem> NamingState<T, S> createRoot(
DexItemFactory itemFactory,
List<String> dictionary,
Function<T, S> keyTransform,
+ MemberNamingStrategy strategy,
boolean useUniqueMemberNames) {
- return new NamingState<>(null, itemFactory, dictionary, keyTransform, useUniqueMemberNames);
+ return new NamingState<>(
+ null, itemFactory, dictionary, keyTransform, strategy, useUniqueMemberNames);
}
private NamingState(
@@ -42,16 +46,19 @@
DexItemFactory itemFactory,
List<String> dictionary,
Function<ProtoType, KeyType> keyTransform,
+ MemberNamingStrategy strategy,
boolean useUniqueMemberNames) {
this.parent = parent;
this.itemFactory = itemFactory;
this.dictionary = dictionary;
this.keyTransform = keyTransform;
+ this.strategy = strategy;
this.useUniqueMemberNames = useUniqueMemberNames;
}
public NamingState<ProtoType, KeyType> createChild() {
- return new NamingState<>(this, itemFactory, dictionary, keyTransform, useUniqueMemberNames);
+ return new NamingState<>(
+ this, itemFactory, dictionary, keyTransform, strategy, useUniqueMemberNames);
}
private InternalState findInternalStateFor(KeyType key) {
@@ -85,12 +92,13 @@
return state.getAssignedNameFor(name, key);
}
- public DexString assignNewNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
+ public DexString assignNewNameFor(
+ DexReference source, DexString original, ProtoType proto, boolean markAsUsed) {
KeyType key = keyTransform.apply(proto);
DexString result = getAssignedNameFor(original, key);
if (result == null) {
InternalState state = getOrCreateInternalStateFor(key);
- result = state.getNameFor(original, key, markAsUsed);
+ result = state.getNameFor(source, original, key, markAsUsed);
}
return result;
}
@@ -146,7 +154,6 @@
class InternalState {
private static final int INITIAL_NAME_COUNT = 1;
- private final char[] EMPTY_CHAR_ARRAY = new char[0];
protected final DexItemFactory itemFactory;
private final InternalState parentInternalState;
@@ -193,6 +200,10 @@
reservedNames.add(name);
}
+ public int incrementAndGet() {
+ return nameCount++;
+ }
+
DexString getAssignedNameFor(DexString original, KeyType proto) {
DexString result = null;
if (renamings != null) {
@@ -215,13 +226,14 @@
return result;
}
- DexString getNameFor(DexString original, KeyType proto, boolean markAsUsed) {
+ DexString getNameFor(
+ DexReference source, DexString original, KeyType proto, boolean markAsUsed) {
DexString name = getAssignedNameFor(original, proto);
if (name != null) {
return name;
}
do {
- name = itemFactory.createString(nextSuggestedName());
+ name = nextSuggestedName(source);
} while (!isAvailable(name));
if (markAsUsed) {
addRenaming(original, proto, name);
@@ -236,11 +248,11 @@
renamings.put(original, proto, newName);
}
- String nextSuggestedName() {
- if (dictionaryIterator.hasNext()) {
- return dictionaryIterator.next();
+ DexString nextSuggestedName(DexReference source) {
+ if (!strategy.bypassDictionary() && dictionaryIterator.hasNext()) {
+ return itemFactory.createString(dictionaryIterator.next());
} else {
- return StringUtils.numberToIdentifier(EMPTY_CHAR_ARRAY, nameCount++, false);
+ return strategy.next(source, this);
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
index b0e2f4c..d95f299 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
@@ -230,6 +230,11 @@
}
private DexType applyClassMappingOnTheFly(DexType from) {
+ if (from.isArrayType()) {
+ DexType baseType = from.toBaseType(appInfo.dexItemFactory);
+ DexType appliedBaseType = applyClassMappingOnTheFly(baseType);
+ return from.replaceBaseType(appliedBaseType, appInfo.dexItemFactory);
+ }
if (seedMapper.hasMapping(from)) {
DexType appliedType = lenseBuilder.lookup(from);
if (appliedType != from) {
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index fd8508b..20f4bc2 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -188,8 +188,16 @@
// Pick the renamed inner class from the fully renamed binary name.
String fullRenamedBinaryName =
getClassBinaryNameFromDescriptor(renamedDescriptor.toString());
- renamedSignature.append(
- fullRenamedBinaryName.substring(enclosingRenamedBinaryName.length() + 1));
+ int innerClassPos = enclosingRenamedBinaryName.length() + 1;
+ if (innerClassPos < fullRenamedBinaryName.length()) {
+ renamedSignature.append(fullRenamedBinaryName.substring(innerClassPos));
+ } else {
+ reporter.warning(
+ new StringDiagnostic(
+ "Should have retained InnerClasses attribute of " + type + ".",
+ appView.appInfo().originFor(type)));
+ renamedSignature.append(name);
+ }
} else {
// Did not find the class - keep the inner class name as is.
// TODO(110085899): Warn about missing classes in signatures?
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 3fa9db5..25e0832 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationElement;
@@ -18,7 +19,12 @@
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
public class AnnotationRemover {
@@ -26,12 +32,18 @@
private final GraphLense lense;
private final ProguardKeepAttributes keep;
private final InternalOptions options;
+ private final Set<DexType> classesToRetainInnerClassAttributeFor;
- public AnnotationRemover(AppInfoWithLiveness appInfo, GraphLense lense, InternalOptions options) {
+ public AnnotationRemover(
+ AppInfoWithLiveness appInfo,
+ GraphLense lense,
+ InternalOptions options,
+ Set<DexType> classesToRetainInnerClassAttributeFor) {
this.appInfo = appInfo;
this.lense = lense;
this.keep = options.getProguardConfiguration().getKeepAttributes();
this.options = options;
+ this.classesToRetainInnerClassAttributeFor = classesToRetainInnerClassAttributeFor;
}
/**
@@ -140,6 +152,79 @@
return this;
}
+ private static boolean hasGenericEnclosingClass(
+ DexProgramClass clazz,
+ Map<DexType, DexProgramClass> enclosingClasses,
+ Set<DexProgramClass> genericClasses) {
+ while (true) {
+ DexProgramClass enclosingClass = enclosingClasses.get(clazz.type);
+ if (enclosingClass == null) {
+ return false;
+ }
+ if (genericClasses.contains(enclosingClass)) {
+ return true;
+ }
+ clazz = enclosingClass;
+ }
+ }
+
+ private static boolean hasSignatureAnnotation(DexProgramClass clazz, DexItemFactory itemFactory) {
+ for (DexAnnotation annotation : clazz.annotations.annotations) {
+ if (DexAnnotation.isSignatureAnnotation(annotation, itemFactory)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static Set<DexType> computeClassesToRetainInnerClassAttributeFor(
+ AppInfoWithLiveness appInfo, InternalOptions options) {
+ // In case of minification for certain inner classes we need to retain their InnerClass
+ // attributes because their minified name still needs to be in hierarchical format
+ // (enclosing$inner) otherwise the GenericSignatureRewriter can't produce the correct,
+ // renamed signature.
+
+ // More precisely:
+ // - we're going to retain the InnerClass attribute that refers to the same class as 'inner'
+ // - for live, inner, nonstatic classes
+ // - that are enclosed by a class with a generic signature.
+
+ // In compat mode we always keep all InnerClass attributes (if requested).
+ // If not requested we never keep any. In these cases don't compute eligible classes.
+ if (options.forceProguardCompatibility
+ || !options.getProguardConfiguration().getKeepAttributes().innerClasses) {
+ return Collections.emptySet();
+ }
+
+ // Build lookup table and set of the interesting classes.
+ // enclosingClasses.get(clazz) gives the enclosing class of 'clazz'
+ Map<DexType, DexProgramClass> enclosingClasses = new IdentityHashMap<>();
+ Set<DexProgramClass> genericClasses = Sets.newIdentityHashSet();
+
+ Iterable<DexProgramClass> programClasses = appInfo.classes();
+ for (DexProgramClass clazz : programClasses) {
+ if (hasSignatureAnnotation(clazz, options.itemFactory)) {
+ genericClasses.add(clazz);
+ }
+ for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
+ if ((innerClassAttribute.getAccess() & Constants.ACC_STATIC) == 0
+ && innerClassAttribute.getOuter() == clazz.type) {
+ enclosingClasses.put(innerClassAttribute.getInner(), clazz);
+ }
+ }
+ }
+
+ Set<DexType> result = Sets.newIdentityHashSet();
+ for (DexProgramClass clazz : programClasses) {
+ if (clazz.getInnerClassAttributeForThisClass() != null
+ && appInfo.liveTypes.contains(clazz.type)
+ && hasGenericEnclosingClass(clazz, enclosingClasses, genericClasses)) {
+ result.add(clazz.type);
+ }
+ }
+ return result;
+ }
+
public void run() {
for (DexProgramClass clazz : appInfo.classes()) {
stripAttributes(clazz);
@@ -208,6 +293,15 @@
return false;
}
+ private static boolean hasInnerClassesFromSet(DexProgramClass clazz, Set<DexType> innerClasses) {
+ for (InnerClassAttribute attr : clazz.getInnerClasses()) {
+ if (attr.getOuter() == clazz.type && innerClasses.contains(attr.getInner())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void stripAttributes(DexProgramClass clazz) {
// If [clazz] is mentioned by a keep rule, it could be used for reflection, and we therefore
// need to keep the enclosing method and inner classes attributes, if requested. In Proguard
@@ -215,15 +309,40 @@
// To ensure reflection from both inner to outer and and outer to inner for kept classes - even
// if only one side is kept - keep the attributes is any class mentioned in these attributes
// is kept.
- if (appInfo.isPinned(clazz.type)
- || enclosingMethodPinned(clazz)
- || innerClassPinned(clazz)
- || options.forceProguardCompatibility) {
+ boolean keptAnyway =
+ appInfo.isPinned(clazz.type)
+ || enclosingMethodPinned(clazz)
+ || innerClassPinned(clazz)
+ || options.forceProguardCompatibility;
+ boolean keepForThisInnerClass = false;
+ boolean keepForThisEnclosingClass = false;
+ if (!keptAnyway) {
+ keepForThisInnerClass = classesToRetainInnerClassAttributeFor.contains(clazz.type);
+ keepForThisEnclosingClass =
+ hasInnerClassesFromSet(clazz, classesToRetainInnerClassAttributeFor);
+ }
+ if (keptAnyway || keepForThisInnerClass || keepForThisEnclosingClass) {
if (!keep.enclosingMethod) {
clazz.clearEnclosingMethod();
}
if (!keep.innerClasses) {
clazz.clearInnerClasses();
+ } else if (!keptAnyway) {
+ // We're keeping this only because of classesToRetainInnerClassAttributeFor.
+ final boolean finalKeepForThisInnerClass = keepForThisInnerClass;
+ final boolean finalKeepForThisEnclosingClass = keepForThisEnclosingClass;
+ clazz.removeInnerClasses(
+ ica -> {
+ if (finalKeepForThisInnerClass && ica.getInner() == clazz.type) {
+ return false;
+ }
+ if (finalKeepForThisEnclosingClass
+ && ica.getOuter() == clazz.type
+ && classesToRetainInnerClassAttributeFor.contains(ica.getInner())) {
+ return false;
+ }
+ return true;
+ });
}
} else {
// These attributes are only relevant for reflection, and this class is not used for
@@ -232,5 +351,4 @@
clazz.clearInnerClasses();
}
}
-
}
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 c0cebeb..3fbb72c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -393,8 +393,8 @@
private void enqueueFirstNonSerializableClassInitializer(DexClass clazz, KeepReason reason) {
assert clazz.isProgramClass() && clazz.isSerializable(appInfo);
- // Clime up the class hierarchy. Break out if the definition is not found, or hit the library
- // classes, which are kept by definition, or encounter the first non-serializable class.
+ // Climb up the class hierarchy. Break out if the definition is not found, or hit the library
+ // classes which are kept by definition, or encounter the first non-serializable class.
while (clazz != null && clazz.isProgramClass() && clazz.isSerializable(appInfo)) {
clazz = appInfo.definitionFor(clazz.superType);
}
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 fe401a1..2152d3c 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1069,12 +1069,12 @@
method -> {
if (method.isObsolete()) {
method.unsetObsolete();
- if (method.hasCode()) {
- method.getCode().setOwner(method);
- }
+ }
+ if (method.hasCode()) {
+ method.getCode().setOwner(method);
}
});
- assert Streams.stream(target.methods())
+ assert Streams.concat(Streams.stream(source.methods()), Streams.stream(target.methods()))
.allMatch(
method ->
!method.isObsolete()
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
index 2253f1f..3f0d0be 100644
--- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -205,11 +206,11 @@
public void testRegress37658666() throws Exception {
runTest(
"regress_37658666.Regress",
- (expectedBytes, actualBytes) -> {
+ (expected, actual) -> {
// javac emits LDC(-0.0f) instead of the shorter FCONST_0 FNEG emitted by CfConstNumber.
String ldc = "methodVisitor.visitLdcInsn(new Float(\"-0.0\"));";
String constNeg = "methodVisitor.visitInsn(FCONST_0);\nmethodVisitor.visitInsn(FNEG);";
- assertEquals(asmToString(expectedBytes).replace(ldc, constNeg), asmToString(actualBytes));
+ assertEquals(expected.replace(ldc, constNeg), actual);
});
}
@@ -294,10 +295,12 @@
}
private void runTest(String clazz) throws Exception {
- runTest(clazz, null);
+ runTest(
+ clazz, (expected, actual) -> assertEquals("Class " + clazz + " differs", expected, actual));
}
- private void runTest(String clazz, BiConsumer<byte[], byte[]> comparator) throws Exception {
+ private void runTest(String clazz, BiConsumer<String, String> comparator) throws Exception {
+ assert comparator != null;
String pkg = clazz.substring(0, clazz.lastIndexOf('.'));
String suffix = "_debuginfo_all";
Path inputJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, pkg + suffix + JAR_EXTENSION);
@@ -324,17 +327,30 @@
for (String descriptor : expected.getClassDescriptors()) {
byte[] expectedBytes = getClassAsBytes(expected, descriptor);
byte[] actualBytes = getClassAsBytes(actual, descriptor);
- if (comparator != null) {
- comparator.accept(expectedBytes, actualBytes);
- } else if (!Arrays.equals(expectedBytes, actualBytes)) {
- assertEquals(
- "Class " + descriptor + " differs",
- asmToString(expectedBytes),
- asmToString(actualBytes));
+ if (!Arrays.equals(expectedBytes, actualBytes)) {
+ String expectedString = replaceCatchThrowableByCatchAll(asmToString(expectedBytes));
+ String actualString = asmToString(actualBytes);
+ comparator.accept(expectedString, actualString);
}
}
}
+ private static String replaceCatchThrowableByCatchAll(String content) {
+ String catchThrowablePrefix = "methodVisitor.visitTryCatchBlock(";
+ String catchThrowableSuffix = ", \"java/lang/Throwable\");";
+ StringBuilder expected = new StringBuilder();
+ List<String> expectedLines = StringUtils.splitLines(content);
+ for (String line : expectedLines) {
+ if (line.startsWith(catchThrowablePrefix) && line.endsWith(catchThrowableSuffix)) {
+ expected.append(line.replace("\"java/lang/Throwable\"", "null"));
+ } else {
+ expected.append(line);
+ }
+ expected.append(StringUtils.LINE_SEPARATOR);
+ }
+ return expected.toString();
+ }
+
private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) {
ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors());
Collections.sort(descriptorList);
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 5b5cf9d..a74146d 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -115,6 +115,7 @@
// Test that required to set min-api to a specific value.
private static Map<String, AndroidApiLevel> needMinSdkVersion =
new ImmutableMap.Builder<String, AndroidApiLevel>()
+ .put("004-JniTest", AndroidApiLevel.N)
// Android O
.put("952-invoke-custom", AndroidApiLevel.O)
.put("952-invoke-custom-kinds", AndroidApiLevel.O)
@@ -132,10 +133,12 @@
.put("162-method-resolution", AndroidApiLevel.N)
.put("616-cha-interface-default", AndroidApiLevel.N)
.put("1910-transform-with-default", AndroidApiLevel.N)
+ .put("960-default-smali", AndroidApiLevel.N)
// Interface initializer is not triggered after desugaring.
.put("962-iface-static", AndroidApiLevel.N)
// Interface initializer is not triggered after desugaring.
.put("964-default-iface-init-gen", AndroidApiLevel.N)
+ .put("966-default-conflict", AndroidApiLevel.N)
// AbstractMethodError (for method not implemented in class) instead of
// IncompatibleClassChangeError (for conflict of default interface methods).
.put("968-default-partial-compile-gen", AndroidApiLevel.N)
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
index fbf630c..abd1b5f 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
@@ -38,20 +38,6 @@
return builder;
}
- static R8Command.Builder loadProgramFiles(Backend backend, Package p, Class... classes)
- throws Exception {
- R8Command.Builder builder = R8Command.builder();
- builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(p));
- for (Class clazz : classes) {
- builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
- }
- builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
- if (backend == Backend.DEX) {
- builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
- }
- return builder;
- }
-
void compareReferenceJVMAndProcessed(AndroidApp app, Class mainClass) throws Exception {
// Run on Jvm.
String jvmOutput = runOnJava(mainClass);
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
index 44149e0..7ab3625 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
@@ -6,7 +6,6 @@
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.R8Command;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.accessrelaxation.privateinstance.Base;
@@ -18,8 +17,6 @@
import com.android.tools.r8.accessrelaxation.privatestatic.BB;
import com.android.tools.r8.accessrelaxation.privatestatic.C;
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.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -67,21 +64,22 @@
+ "java.lang.IllegalAccessErrorB::bar() >> java.lang.IllegalAccessError",
"B::foo()A::foo()A::baz()A::bar()A::bar(int)",
"C::blah(int)");
-
+ Class<?> mainClass = C.class;
if (backend == Backend.CF) {
// Only run JVM reference once (for CF backend)
- testForJvm().addTestClasspath().run(C.class).assertSuccessWithOutput(expectedOutput);
+ testForJvm().addTestClasspath().run(mainClass).assertSuccessWithOutput(expectedOutput);
}
R8TestRunResult result =
testForR8(backend)
- .addProgramFiles(ToolHelper.getClassFilesForTestPackage(C.class.getPackage()))
+ .addProgramFiles(ToolHelper.getClassFilesForTestPackage(mainClass.getPackage()))
.addOptionsModification(o -> o.enableArgumentRemoval = enableArgumentRemoval)
+ .noMinification()
.addKeepRules(
// Note: we use '-checkdiscard' to indirectly check that the access relaxation is
// done which leads to inlining of all pB*** methods so they are removed. Without
// access relaxation inlining is not performed and method are kept.
- "-keep class " + C.class.getCanonicalName() + "{",
+ "-keep class " + mainClass.getCanonicalName() + "{",
" public static void main(java.lang.String[]);",
"}",
"",
@@ -100,9 +98,8 @@
" *** pBlah1();",
"}",
"",
- "-dontobfuscate",
"-allowaccessmodification")
- .run(C.class);
+ .run(mainClass);
assertEquals(
expectedOutput,
@@ -134,42 +131,60 @@
}
private void testInstanceMethodRelaxation(boolean enableVerticalClassMerging) throws Exception {
- Class mainClass = TestMain.class;
- R8Command.Builder builder = loadProgramFiles(backend, mainClass.getPackage());
+ String expectedOutput =
+ StringUtils.lines(
+ "Base::foo()",
+ "Base::foo1()",
+ "Base::foo2()",
+ "Sub1::foo1()",
+ "Itf1::foo1(0) >> Sub1::foo1()",
+ "Sub1::bar1(0)",
+ "Sub2::foo2()",
+ "Itf2::foo2(0) >> Sub2::foo2()",
+ "Sub2::bar2(0)");
+ Class<?> mainClass = TestMain.class;
+ if (backend == Backend.CF) {
+ // Only run JVM reference once (for CF backend)
+ testForJvm().addTestClasspath().run(mainClass).assertSuccessWithOutput(expectedOutput);
+ }
- builder.addProguardConfiguration(
- ImmutableList.of(
- "-keep class " + mainClass.getCanonicalName() + "{",
- " public static void main(java.lang.String[]);",
- "}",
- "",
- "-checkdiscard class " + Base.class.getCanonicalName() + "{",
- " *** p*();",
- "}",
- "",
- "-checkdiscard class " + Sub1.class.getCanonicalName() + "{",
- " *** p*();",
- "}",
- "",
- "-checkdiscard class " + Sub2.class.getCanonicalName() + "{",
- " *** p*();",
- "}",
- "",
- "-dontobfuscate",
- "-allowaccessmodification"
- ),
- Origin.unknown());
+ R8TestRunResult result =
+ testForR8(backend)
+ .addProgramFiles(ToolHelper.getClassFilesForTestPackage(mainClass.getPackage()))
+ .addOptionsModification(o -> o.enableVerticalClassMerging = enableVerticalClassMerging)
+ .enableInliningAnnotations()
+ .noMinification()
+ .addKeepRules(
+ "-keep class " + mainClass.getCanonicalName() + "{",
+ " public static void main(java.lang.String[]);",
+ "}",
+ "",
+ "-checkdiscard class " + Base.class.getCanonicalName() + "{",
+ " *** p*();",
+ "}",
+ "",
+ "-checkdiscard class " + Sub1.class.getCanonicalName() + "{",
+ " *** p*();",
+ "}",
+ "",
+ "-checkdiscard class " + Sub2.class.getCanonicalName() + "{",
+ " *** p*();",
+ "}",
+ "",
+ "-allowaccessmodification"
+ )
+ .run(mainClass);
- AndroidApp app =
- ToolHelper.runR8(
- builder.build(),
- options -> options.enableVerticalClassMerging = enableVerticalClassMerging);
- compareReferenceJVMAndProcessed(app, mainClass);
+ assertEquals(
+ expectedOutput,
+ result
+ .getStdOut()
+ .replace("java.lang.IncompatibleClassChangeError", "java.lang.IllegalAccessError"));
// When vertical class merging is enabled, Itf1 is merged into Sub1 and Itf2 is merged into
// Sub2, and as a result of these merges, neither Sub1 nor Sub2 end up in the output because of
// inlining.
- CodeInspector codeInspector = new CodeInspector(app);
+ CodeInspector codeInspector = result.inspector();
assertPublic(codeInspector, Base.class, new MethodSignature("foo", STRING, ImmutableList.of()));
// Base#foo?() can't be publicized due to Itf<1>#foo<1>().
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Base.java b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Base.java
index 6325c78..130ba9a 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Base.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Base.java
@@ -3,10 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.accessrelaxation.privateinstance;
+import com.android.tools.r8.NeverInline;
+
public class Base {
- // NOTE: here and below 'synchronized' is supposed to disable inlining of this method.
- private synchronized String foo() {
+ @NeverInline
+ private String foo() {
return "Base::foo()";
}
@@ -14,7 +16,8 @@
return foo();
}
- private synchronized String foo1() {
+ @NeverInline
+ private String foo1() {
return "Base::foo1()";
}
@@ -22,7 +25,8 @@
return foo1();
}
- private synchronized String foo2() {
+ @NeverInline
+ private String foo2() {
return "Base::foo2()";
}
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub1.java b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub1.java
index 317aaff..0793001 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub1.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub1.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.accessrelaxation.privateinstance;
+import com.android.tools.r8.NeverInline;
+
public class Sub1 extends Base implements Itf1 {
@Override
@@ -10,7 +12,8 @@
return "Sub1::foo1()";
}
- private synchronized String bar1(int i) {
+ @NeverInline
+ private String bar1(int i) {
return "Sub1::bar1(" + i + ")";
}
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub2.java b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub2.java
index 039e3ac..018bc90 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub2.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub2.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.accessrelaxation.privateinstance;
+import com.android.tools.r8.NeverInline;
+
public class Sub2 extends Base implements Itf2 {
@Override
@@ -10,7 +12,8 @@
return "Sub2::foo2()";
}
- private synchronized String bar1(int i) {
+ @NeverInline
+ private String bar1(int i) {
return "Sub2::bar1(" + i + ")";
}
@@ -18,7 +21,8 @@
return bar1(1);
}
- private synchronized String bar2(int i) {
+ @NeverInline
+ private String bar2(int i) {
return "Sub2::bar2(" + i + ")";
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodTest.java
new file mode 100644
index 0000000..3693f87
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugaring.interfacemethods;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+
+public class InvokeSuperInDefaultInterfaceMethodTest extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("I.m()", "J.m()", "JImpl.m()", "I.m()", "KImpl.m()");
+
+ testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+
+ testForR8(Backend.DEX)
+ .addInnerClasses(InvokeSuperInDefaultInterfaceMethodTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableClassInliningAnnotations()
+ .enableMergeAnnotations()
+ .setMinApi(AndroidApiLevel.M)
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new JImpl().m();
+ new KImpl().m();
+ }
+ }
+
+ @NeverMerge
+ interface I {
+
+ default void m() {
+ System.out.println("I.m()");
+ }
+ }
+
+ @NeverMerge
+ interface J extends I {
+
+ @Override
+ default void m() {
+ I.super.m();
+ System.out.println("J.m()");
+ }
+ }
+
+ @NeverMerge
+ interface K extends I {
+
+ // Intentionally does not override I.m().
+ }
+
+ @NeverClassInline
+ static class JImpl implements J {
+
+ @Override
+ public void m() {
+ J.super.m();
+ System.out.println("JImpl.m()");
+ }
+ }
+
+ @NeverClassInline
+ static class KImpl implements K {
+
+ @Override
+ public void m() {
+ K.super.m();
+ System.out.println("KImpl.m()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
new file mode 100644
index 0000000..698cf50
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugaring.interfacemethods;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest extends TestBase {
+
+ private final boolean includeInterfaceMethodOnJ;
+
+ @Parameterized.Parameters(name = "Include interface method on J: {0}")
+ public static Boolean[] data() {
+ return BooleanUtils.values();
+ }
+
+ public InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest(
+ boolean includeInterfaceMethodOnJ) {
+ this.includeInterfaceMethodOnJ = includeInterfaceMethodOnJ;
+ }
+
+ @Test
+ public void test() throws Exception {
+ // Note that the expected output is independent of the presence of J.m().
+ String expectedOutput = StringUtils.lines("I.m()", "JImpl.m()");
+
+ byte[] dex = buildProgramDexFileData();
+ if (ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1)) {
+ AndroidApp app =
+ AndroidApp.builder()
+ .addDexProgramData(buildProgramDexFileData(), Origin.unknown())
+ .build();
+ assertEquals(expectedOutput, runOnArt(app, "TestClass"));
+ }
+
+ testForR8(Backend.DEX)
+ .addProgramDexFileData(dex)
+ .addKeepMainRule("TestClass")
+ .setMinApi(AndroidApiLevel.M)
+ .run("TestClass")
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ // Using Smali instead of Jasmin because interfaces are broken in Jasmin.
+ private byte[] buildProgramDexFileData() throws Exception {
+ SmaliBuilder smaliBuilder = new SmaliBuilder();
+ smaliBuilder.setMinApi(AndroidApiLevel.N);
+
+ smaliBuilder.addClass("TestClass");
+
+ // public void main(String[] args) { new JImpl().m(); }
+ smaliBuilder.addMainMethod(
+ 1,
+ "new-instance v0, LJImpl;",
+ "invoke-direct {v0}, LJImpl;-><init>()V",
+ "invoke-virtual {v0}, LJImpl;->m()V",
+ "return-void");
+
+ smaliBuilder.addInterface("I");
+
+ // default void m() { System.out.println("In I.m()"); }
+ smaliBuilder.addInstanceMethod(
+ "void",
+ "m",
+ 2,
+ "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ "const-string v1, \"I.m()\"",
+ "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+ "return-void");
+
+ smaliBuilder.addInterface("J", "java.lang.Object", ImmutableList.of("I"));
+ if (includeInterfaceMethodOnJ) {
+ smaliBuilder.addInstanceMethod(
+ "void",
+ "m",
+ 2,
+ "invoke-super {p0}, LI;->m()V",
+ "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ "const-string v1, \"J.m()\"",
+ "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+ "return-void");
+ }
+
+ smaliBuilder.addClass("JImpl", "java.lang.Object", ImmutableList.of("J"));
+ smaliBuilder.addDefaultConstructor();
+
+ // default void m() { I.super.m(); System.out.println("In JImpl.m()"); }
+ smaliBuilder.addInstanceMethod(
+ "void",
+ "m",
+ 2,
+ // Note: invoke-super instruction to the non-immediate interface I.
+ "invoke-super {p0}, LI;->m()V",
+ "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ "const-string v1, \"JImpl.m()\"",
+ "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+ "return-void");
+
+ return smaliBuilder.compile();
+ }
+}
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 a9e2157..37b0cdc 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
@@ -132,9 +132,7 @@
new Value(
0,
TypeLatticeElement.fromDexType(
- DexItemFactory.catchAllType,
- Nullability.definitelyNotNull(),
- appInfo),
+ app.dexItemFactory.throwableType, Nullability.definitelyNotNull(), appInfo),
null);
instruction = new Argument(value);
instruction.setPosition(position);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 532a748..b69569f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -94,6 +94,7 @@
String javaOutput = runOnJava(main);
TestRunResult result = testForR8(backend)
.addProgramClasses(classes)
+ .enableInliningAnnotations()
.addKeepMainRule(main)
.addKeepRules(
"-dontobfuscate", "-allowaccessmodification", "-keepattributes LineNumberTable")
@@ -172,7 +173,6 @@
String javaOutput = runOnJava(main);
TestRunResult result = testForR8(backend)
.addProgramClasses(classes)
- .enableProguardTestOptions()
.enableInliningAnnotations()
.addKeepMainRule(main)
.addKeepRules(
@@ -277,6 +277,7 @@
String javaOutput = runOnJava(main);
TestRunResult result = testForR8(backend)
.addProgramClasses(classes)
+ .enableInliningAnnotations()
.addKeepMainRule(main)
.addKeepRules(
"-dontobfuscate", "-allowaccessmodification", "-keepattributes LineNumberTable")
@@ -316,6 +317,8 @@
String javaOutput = runOnJava(main);
TestRunResult result = testForR8(backend)
.addProgramClasses(classes)
+ .enableProguardTestOptions()
+ .enableInliningAnnotations()
.addKeepMainRule(main)
.addKeepRules(
"-dontobfuscate", "-allowaccessmodification", "-keepattributes LineNumberTable")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java
index 38e75c4..dcfb5eb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.classinliner.code;
+import com.android.tools.r8.NeverInline;
+
public class C {
public static class L {
public final int x;
@@ -25,15 +27,18 @@
}
}
- public synchronized static int method1() {
+ @NeverInline
+ public static int method1() {
return new L(1).x;
}
- public synchronized static int method2() {
+ @NeverInline
+ public static int method2() {
return new L(1).getX();
}
- public synchronized static int method3() {
+ @NeverInline
+ public static int method3() {
return F.I.getX();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
index 26dc8cc..8bba53e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.classinliner.invalidroot;
+import com.android.tools.r8.NeverInline;
+
public class InvalidRootsTestClass {
private static int ID = 0;
@@ -19,7 +21,8 @@
test.testRootInvalidatesAfterInlining();
}
- private synchronized void testExtraNeverReturnsNormally() {
+ @NeverInline
+ private void testExtraNeverReturnsNormally() {
testExtraNeverReturnsNormallyA();
testExtraNeverReturnsNormallyB();
@@ -31,7 +34,8 @@
}
}
- private synchronized void testExtraNeverReturnsNormallyA() {
+ @NeverInline
+ private void testExtraNeverReturnsNormallyA() {
try {
neverReturnsNormallyExtra(next(), null);
} catch (RuntimeException re) {
@@ -39,7 +43,8 @@
}
}
- private synchronized void testExtraNeverReturnsNormallyB() {
+ @NeverInline
+ private void testExtraNeverReturnsNormallyB() {
try {
neverReturnsNormallyExtra(next(), null);
} catch (RuntimeException re) {
@@ -47,7 +52,8 @@
}
}
- private synchronized void testDirectNeverReturnsNormally() {
+ @NeverInline
+ private void testDirectNeverReturnsNormally() {
try {
NeverReturnsNormally a = new NeverReturnsNormally();
System.out.println(a.foo());
@@ -56,7 +62,8 @@
}
}
- private synchronized void testInitNeverReturnsNormally() {
+ @NeverInline
+ private void testInitNeverReturnsNormally() {
try {
new InitNeverReturnsNormally();
} catch (RuntimeException re) {
@@ -85,7 +92,8 @@
}
}
- private synchronized void testRootInvalidatesAfterInlining() {
+ @NeverInline
+ private void testRootInvalidatesAfterInlining() {
A a = new A();
try {
notInlinedExtraMethod(next(), a);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
index 7e3cba2..a2576bf 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.classinliner.trivial;
+import com.android.tools.r8.NeverInline;
+
public class TrivialTestClass {
private static int ID = 0;
@@ -25,35 +27,42 @@
test.testCycles();
}
- private synchronized void testInner() {
+ @NeverInline
+ private void testInner() {
Inner inner = new Inner("inner{", 123, next() + "}");
System.out.println(inner.toString() + " " + inner.getPrefix() + " = " + inner.prefix);
}
- private synchronized void testConstructorMapping1() {
+ @NeverInline
+ private void testConstructorMapping1() {
ReferencedFields o = new ReferencedFields(next());
System.out.println(o.getA());
}
- private synchronized void testConstructorMapping2() {
+ @NeverInline
+ private void testConstructorMapping2() {
ReferencedFields o = new ReferencedFields(next());
System.out.println(o.getB());
}
- private synchronized void testConstructorMapping3() {
+ @NeverInline
+ private void testConstructorMapping3() {
ReferencedFields o = new ReferencedFields(next(), next());
System.out.println(o.getA() + o.getB() + o.getConcat());
}
- private synchronized void testEmptyClass() {
+ @NeverInline
+ private void testEmptyClass() {
new EmptyClass();
}
- private synchronized void testEmptyClassWithInitializer() {
+ @NeverInline
+ private void testEmptyClassWithInitializer() {
new EmptyClassWithInitializer();
}
- private synchronized void testClassWithFinalizer() {
+ @NeverInline
+ private void testClassWithFinalizer() {
new ClassWithFinal();
}
@@ -61,7 +70,8 @@
iface.foo();
}
- private synchronized void testCallOnIface1() {
+ @NeverInline
+ private void testCallOnIface1() {
callOnIface1(new Iface1Impl(next()));
}
@@ -69,14 +79,18 @@
iface.foo();
}
- private synchronized void testCallOnIface2() {
+ @NeverInline
+ private void testCallOnIface2() {
callOnIface2(new Iface2Impl(next()));
System.out.println(Iface2Impl.CONSTANT); // Keep constant referenced
}
- private synchronized void testCycles() {
+ @NeverInline
+ private void testCycles() {
new CycleReferenceAB("first").foo(3);
new CycleReferenceBA("second").foo(4);
+ new CycleReferenceAB("third").foo(5);
+ new CycleReferenceBA("fourth").foo(6);
}
public class Inner {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java
new file mode 100644
index 0000000..76e9cfa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.inliner.interfacemethods;
+
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.DEFAULT_METHOD_PREFIX;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+
+public class InlineDefaultInterfaceMethodTest extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("Hello world!");
+
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .addInnerClasses(InlineDefaultInterfaceMethodTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(AndroidApiLevel.M)
+ .enableClassInliningAnnotations()
+ .enableMergeAnnotations()
+ .minification(false)
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput)
+ .inspector();
+
+ // TODO(b/124017330): interface methods should have been inlined into C.method().
+ ClassSubject classSubject =
+ inspector.clazz(I.class.getTypeName() + COMPANION_CLASS_NAME_SUFFIX);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject.uniqueMethodWithName(DEFAULT_METHOD_PREFIX + "hello"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName(DEFAULT_METHOD_PREFIX + "space"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName(DEFAULT_METHOD_PREFIX + "world"), isPresent());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ C obj = new C();
+ obj.method();
+ }
+ }
+
+ @NeverMerge
+ interface I {
+
+ default void hello() {
+ System.out.print("Hello");
+ }
+
+ default void space() {
+ System.out.print(" ");
+ }
+
+ default void world() {
+ System.out.println("world!");
+ }
+ }
+
+ @NeverClassInline
+ static class C implements I {
+
+ @NeverInline
+ public void method() {
+ // invoke-virtual
+ hello();
+ // invoke-interface
+ I self = this;
+ self.space();
+ // invoke-super
+ I.super.world();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineStaticInterfaceMethodTest.java
similarity index 95%
rename from src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineStaticInterfaceMethodTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineStaticInterfaceMethodTest.java
index af29bc7..fa636d5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineStaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineStaticInterfaceMethodTest.java
@@ -2,7 +2,7 @@
// 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.inliner;
+package com.android.tools.r8.ir.optimize.inliner.interfacemethods;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java
new file mode 100644
index 0000000..a73b766
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.string;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
+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.Streams;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+class NestedStringBuilders {
+
+ public static void main(String... args) {
+ System.out.println(concat("one", args[0]) + "two");
+ }
+
+ @ForceInline
+ public static String concat(String one, String two) {
+ return one + two;
+ }
+}
+
+@RunWith(Parameterized.class)
+public class NestedStringBuilderTest extends TestBase {
+ private static final Class<?> MAIN = NestedStringBuilders.class;
+ private final Backend backend;
+
+ @Parameterized.Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ public NestedStringBuilderTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ private void test(TestCompileResult result) throws Exception {
+ CodeInspector codeInspector = result.inspector();
+ ClassSubject mainClass = codeInspector.clazz(MAIN);
+ MethodSubject main = mainClass.mainMethod();
+ long count = Streams.stream(main.iterateInstructions(instructionSubject ->
+ instructionSubject.isNewInstance(StringBuilder.class.getTypeName()))).count();
+ // TODO(b/113859361): should be 1 after merging StringBuilder's
+ assertEquals(2, count);
+ }
+
+ @Test
+ public void b113859361() throws Exception {
+ TestCompileResult result = testForR8(backend)
+ .addProgramClasses(MAIN)
+ .enableInliningAnnotations()
+ .addKeepMainRule(MAIN)
+ .compile();
+ test(result);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java
index a9cf86e..bd7bb01 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java
@@ -20,7 +20,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import java.util.List;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -104,7 +103,6 @@
}
@Test
- @Ignore("b/119399513")
public void testD8() throws Exception {
assumeTrue("Only run D8 for Dex backend", backend == Backend.DEX);
@@ -124,7 +122,6 @@
}
@Test
- @Ignore("b/119399513")
public void testR8() throws Exception {
TestRunResult result = testForR8(backend)
.addProgramClasses(CLASSES)
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 19c931e..b722683 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -4,15 +4,18 @@
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.Code;
@@ -24,7 +27,6 @@
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;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -32,7 +34,6 @@
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -42,63 +43,65 @@
import org.junit.Assume;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public abstract class AbstractR8KotlinTestBase extends TestBase {
+public abstract class AbstractR8KotlinTestBase extends KotlinTestBase {
// This is the name of the Jasmin-generated class which contains the "main" method which will
// invoke the tested method.
private static final String JASMIN_MAIN_CLASS = "TestMain";
- @Parameter(0) public boolean allowAccessModification;
- @Parameter(1) public KotlinTargetVersion targetVersion;
+ protected final boolean allowAccessModification;
private final List<Path> classpath = new ArrayList<>();
private final List<Path> extraClasspath = new ArrayList<>();
- @Parameters(name = "allowAccessModification: {0} target: {1}")
+ @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
- return buildParameters(BooleanUtils.values(), KotlinTargetVersion.values());
+ return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values());
+ }
+
+ protected AbstractR8KotlinTestBase(
+ KotlinTargetVersion kotlinTargetVersion, boolean allowAccessModification) {
+ super(kotlinTargetVersion);
+ this.allowAccessModification = allowAccessModification;
}
protected void addExtraClasspath(Path path) {
extraClasspath.add(path);
}
- protected static void checkMethodIsInvokedAtLeastOnce(DexCode dexCode,
- MethodSignature... methodSignatures) {
+ protected static void checkMethodIsInvokedAtLeastOnce(
+ DexCode dexCode, MethodSignature... methodSignatures) {
for (MethodSignature methodSignature : methodSignatures) {
checkMethodIsInvokedAtLeastOnce(dexCode, methodSignature);
}
}
- private static void checkMethodIsInvokedAtLeastOnce(DexCode dexCode,
- MethodSignature methodSignature) {
+ private static void checkMethodIsInvokedAtLeastOnce(
+ DexCode dexCode, MethodSignature methodSignature) {
assertTrue("No invoke to '" + methodSignature.toString() + "'",
Arrays.stream(dexCode.instructions)
.filter((instr) -> instr.getMethod() != null)
.anyMatch((instr) -> instr.getMethod().name.toString().equals(methodSignature.name)));
}
- protected static void checkMethodIsNeverInvoked(DexCode dexCode,
- MethodSignature... methodSignatures) {
+ protected static void checkMethodIsNeverInvoked(
+ DexCode dexCode, MethodSignature... methodSignatures) {
for (MethodSignature methodSignature : methodSignatures) {
checkMethodIsNeverInvoked(dexCode, methodSignature);
}
}
- private static void checkMethodIsNeverInvoked(DexCode dexCode,
- MethodSignature methodSignature) {
+ private static void checkMethodIsNeverInvoked(DexCode dexCode, MethodSignature methodSignature) {
assertTrue("At least one invoke to '" + methodSignature.toString() + "'",
Arrays.stream(dexCode.instructions)
.filter((instr) -> instr.getMethod() != null)
.noneMatch((instr) -> instr.getMethod().name.toString().equals(methodSignature.name)));
}
- protected static void checkMethodsPresence(ClassSubject classSubject,
- Map<MethodSignature, Boolean> presenceMap) {
+ protected static void checkMethodsPresence(
+ ClassSubject classSubject, Map<MethodSignature, Boolean> presenceMap) {
presenceMap.forEach(((methodSignature, isPresent) -> {
MethodSubject methodSubject = classSubject.method(methodSignature);
String methodDesc = methodSignature.toString();
@@ -118,8 +121,8 @@
return classSubject;
}
- protected FieldSubject checkFieldIsKept(ClassSubject classSubject, String fieldType,
- String fieldName) {
+ protected FieldSubject checkFieldIsKept(
+ ClassSubject classSubject, String fieldType, String fieldName) {
// Field must exist in the input.
checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, true);
FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
@@ -128,8 +131,13 @@
return fieldSubject;
}
- protected void checkFieldIsAbsent(ClassSubject classSubject, String fieldType,
- String fieldName) {
+ protected FieldSubject checkFieldIsKept(ClassSubject classSubject, String fieldName) {
+ FieldSubject fieldSubject = classSubject.uniqueFieldWithName(fieldName);
+ assertThat(fieldSubject, isPresent());
+ return fieldSubject;
+ }
+
+ protected void checkFieldIsAbsent(ClassSubject classSubject, String fieldType, String fieldName) {
// Field must NOT exist in the input.
checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, false);
FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
@@ -137,32 +145,47 @@
assertFalse(fieldSubject.isPresent());
}
- protected void checkMethodIsAbsent(ClassSubject classSubject,
- MethodSignature methodSignature) {
+ protected FieldSubject checkFieldIsAbsent(ClassSubject classSubject, String fieldName) {
+ FieldSubject fieldSubject = classSubject.uniqueFieldWithName(fieldName);
+ assertThat(fieldSubject, not(isPresent()));
+ return fieldSubject;
+ }
+
+ protected void checkMethodIsAbsent(ClassSubject classSubject, MethodSignature methodSignature) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, false);
checkMethodPresenceInOutput(classSubject, methodSignature, false);
}
- protected MethodSubject checkMethodIsKept(ClassSubject classSubject,
- MethodSignature methodSignature) {
+ protected MethodSubject checkMethodIsKept(
+ ClassSubject classSubject, MethodSignature methodSignature) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
return checkMethodIsKeptOrRemoved(classSubject, methodSignature, true);
}
- protected void checkMethodIsRemoved(ClassSubject classSubject,
- MethodSignature methodSignature) {
+ protected MethodSubject checkMethodIsKept(ClassSubject classSubject, String methodName) {
+ MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
+ assertThat(methodSubject, isPresent());
+ return methodSubject;
+ }
+
+ protected void checkMethodIsRemoved(ClassSubject classSubject, MethodSignature methodSignature) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
checkMethodIsKeptOrRemoved(classSubject, methodSignature, false);
}
- protected MethodSubject checkMethodIsKeptOrRemoved(ClassSubject classSubject,
- MethodSignature methodSignature, boolean isPresent) {
+ protected void checkMethodIsRemoved(ClassSubject classSubject, String methodName) {
+ MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
+ assertThat(methodSubject, not(isPresent()));
+ }
+
+ protected MethodSubject checkMethodIsKeptOrRemoved(
+ ClassSubject classSubject, MethodSignature methodSignature, boolean isPresent) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
return checkMethodPresenceInOutput(classSubject, methodSignature, isPresent);
}
- private MethodSubject checkMethodPresenceInOutput(ClassSubject classSubject,
- MethodSignature methodSignature, boolean isPresent) {
+ private MethodSubject checkMethodPresenceInOutput(
+ ClassSubject classSubject, MethodSignature methodSignature, boolean isPresent) {
MethodSubject methodSubject = classSubject.method(methodSignature);
assertNotNull(methodSubject);
@@ -272,8 +295,8 @@
}
}
- private void checkMethodPresenceInInput(String className, MethodSignature methodSignature,
- boolean isPresent) {
+ private void checkMethodPresenceInInput(
+ String className, MethodSignature methodSignature, boolean isPresent) {
boolean foundMethod = AsmUtils.doesMethodExist(classpath, className,
methodSignature.name, methodSignature.toDescriptor());
if (isPresent != foundMethod) {
@@ -285,8 +308,8 @@
}
}
- private void checkFieldPresenceInInput(String className, String fieldType, String fieldName,
- boolean isPresent) {
+ private void checkFieldPresenceInInput(
+ String className, String fieldType, String fieldName, boolean isPresent) {
boolean foundField = AsmUtils.doesFieldExist(classpath, className, fieldName, fieldType);
if (isPresent != foundField) {
throw new AssertionError(
@@ -296,18 +319,8 @@
}
}
- private Path getKotlinJarFile(String folder) {
- return Paths.get(ToolHelper.TESTS_BUILD_DIR, "kotlinR8TestResources",
- targetVersion.getFolderName(), folder + FileUtils.JAR_EXTENSION);
- }
-
- private Path getJavaJarFile(String folder) {
- return Paths.get(ToolHelper.TESTS_BUILD_DIR, "kotlinR8TestResources",
- targetVersion.getFolderName(), folder + ".java" + FileUtils.JAR_EXTENSION);
- }
-
@FunctionalInterface
- interface AndroidAppInspector {
+ public interface AndroidAppInspector {
void inspectApp(AndroidApp androidApp) throws Exception;
}
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 1a690e2..30d90eb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -10,6 +10,7 @@
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;
@@ -33,6 +34,11 @@
public class KotlinClassInlinerTest extends AbstractR8KotlinTestBase {
+ public KotlinClassInlinerTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
private static boolean isLambda(DexClass clazz) {
return !clazz.type.getPackageDescriptor().startsWith("kotlin") &&
(isKStyleLambdaOrGroup(clazz) || isJStyleLambdaOrGroup(clazz));
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 ced08f5..3f02c70 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -9,6 +9,7 @@
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 java.util.concurrent.atomic.AtomicInteger;
@@ -16,6 +17,11 @@
public class KotlinClassStaticizerTest extends AbstractR8KotlinTestBase {
+ public KotlinClassStaticizerTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testCompanionAndRegularObjects() throws Exception {
assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
index 7e9269d..0b79b3b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
@@ -39,6 +40,11 @@
opts.forceProguardCompatibility = true;
};
+ public KotlinLambdaMergingTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
abstract static class LambdaOrGroup {
abstract boolean match(DexClass clazz);
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
index 343dfdf..7b43112 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.InternalOptions;
import java.util.function.Consumer;
import org.junit.Test;
@@ -17,6 +18,11 @@
o.enableLambdaMerging = true;
};
+ public KotlinLambdaMergingWithReprocessingTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testMergingKStyleLambdasAndReprocessing() throws Exception {
final String mainClassName = "reprocess_merged_lambdas_kstyle.MainKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithFailedInliningTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java
similarity index 71%
rename from src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithFailedInliningTest.java
rename to src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index f17a729..c5ee56d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithFailedInliningTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -3,11 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.InternalOptions;
import java.util.function.Consumer;
import org.junit.Test;
-public class KotlinLambdaMergingWithFailedInliningTest extends AbstractR8KotlinTestBase {
+public class KotlinLambdaMergingWithSmallInliningBudgetTest extends AbstractR8KotlinTestBase {
private Consumer<InternalOptions> optionsModifier =
o -> {
o.enableTreeShaking = true;
@@ -18,6 +19,11 @@
o.inliningInstructionAllowance = 3;
};
+ public KotlinLambdaMergingWithSmallInliningBudgetTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testJStyleRunnable() throws Exception {
final String mainClassName = "lambdas_jstyle_runnable.MainKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 1515689..0048b1b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -64,6 +65,11 @@
private Consumer<InternalOptions> disableClassStaticizer =
opts -> opts.enableClassStaticizer = false;
+ public R8KotlinAccessorTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testCompanionProperty_primitivePropertyIsAlwaysInlined() throws Exception {
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 9224405..7e16b2f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -37,6 +38,11 @@
private Consumer<InternalOptions> disableClassInliner = o -> o.enableClassInlining = false;
+ public R8KotlinDataClassTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void test_dataclass_gettersOnly() throws Exception {
final String mainClassName = "dataclass.MainGettersOnlyKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index c90df04..c74b790 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -20,6 +21,11 @@
private static final TestKotlinDataClass KOTLIN_INTRINSICS_CLASS =
new TestKotlinDataClass("kotlin.jvm.internal.Intrinsics");
+ public R8KotlinIntrinsicsTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testParameterNullCheckIsInlined() throws Exception {
final String extraRules = keepClassMethod("intrinsics.IntrinsicsKt",
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index bc23757..d310c6e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.TestKotlinClass.KotlinProperty;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
@@ -90,6 +91,11 @@
o.enableClassStaticizer = false;
};
+ public R8KotlinPropertiesTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testMutableProperty_getterAndSetterAreRemoveIfNotUsed() throws Exception {
String mainClass = addMainToClasspath("properties/MutablePropertyKt",
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 0ebc393..b4b0c06 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -18,6 +19,11 @@
private static final String FOLDER = "non_null";
private static final String STRING = "java.lang.String";
+ public SimplifyIfNotNullKotlinTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void test_example1() throws Exception {
final TestKotlinClass ex1 = new TestKotlinClass("non_null.Example1Kt");
diff --git a/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java b/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
index 685df6a..d135bf9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
+++ b/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
@@ -16,7 +16,7 @@
*
* <p>See https://kotlinlang.org/docs/reference/classes.html</p>
*/
-class TestKotlinClass {
+public class TestKotlinClass {
/**
* This is the suffix appended by Kotlin compiler to getter and setter method names of
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 30db56e..bdf815e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -888,7 +888,7 @@
}
@Override
- public CatchHandlers<Integer> getCurrentCatchHandlers() {
+ public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
return null;
}
diff --git a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
index ec728b1..967277c 100644
--- a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
+++ b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
@@ -41,6 +42,7 @@
private void runTest(Consumer<CodeInspector> inspection) throws Exception {
R8Command.Builder builder = R8Command.builder();
builder.addProgramFiles(ToolHelper.getClassFileForTestClass(ForceInline.class));
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(NeverInline.class));
builder.addProgramFiles(ToolHelper.getClassFileForTestClass(TestClass.class));
builder.addProgramFiles(ToolHelper.getClassFileForTestClass(Utils.class));
builder.addLibraryFiles(runtimeJar(backend));
@@ -49,6 +51,7 @@
builder.addProguardConfiguration(
ImmutableList.of(
"-forceinline class * { @com.android.tools.r8.ForceInline *; }",
+ "-neverinline class * { @com.android.tools.r8.NeverInline *; }",
"-keep class " + TestClass.class.getCanonicalName() + "{ *; }",
"-dontobfuscate",
"-allowaccessmodification"
diff --git a/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java b/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java
index 086eac6..2af704d 100644
--- a/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java
+++ b/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.movestringconstants;
import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.NeverInline;
public class TestClass {
public static void main(String[] args) {}
@@ -36,7 +37,8 @@
}
}
- private synchronized static void throwException(String message) {
+ @NeverInline
+ private static void throwException(String message) {
throw new RuntimeException(message);
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
new file mode 100644
index 0000000..bb9af02
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+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.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public abstract class AbstractR8KotlinNamingTestBase extends AbstractR8KotlinTestBase {
+
+ protected final boolean minification;
+
+ @Parameters(name = "target: {0}, allowAccessModification: {1}, minification: {2}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ KotlinTargetVersion.values(), BooleanUtils.values(), BooleanUtils.values());
+ }
+
+ AbstractR8KotlinNamingTestBase(
+ KotlinTargetVersion kotlinTargetVersion,
+ boolean allowAccessModification,
+ boolean minification) {
+ super(kotlinTargetVersion, allowAccessModification);
+ this.minification = minification;
+ }
+
+ protected ClassSubject checkClassIsRenamed(CodeInspector inspector, String className) {
+ ClassSubject classSubject = inspector.clazz(className);
+ assertThat(classSubject, isRenamed());
+ return classSubject;
+ }
+
+ protected ClassSubject checkClassIsNotRenamed(CodeInspector inspector, String className) {
+ ClassSubject classSubject = inspector.clazz(className);
+ assertThat(classSubject, not(isRenamed()));
+ return classSubject;
+ }
+
+ protected FieldSubject checkFieldIsRenamed(
+ ClassSubject classSubject, String fieldType, String fieldName) {
+ FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
+ assertThat(fieldSubject, isRenamed());
+ return fieldSubject;
+ }
+
+ protected FieldSubject checkFieldIsRenamed(ClassSubject classSubject, String fieldName) {
+ FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
+ assertThat(fieldSubject, isRenamed());
+ return fieldSubject;
+ }
+
+ protected FieldSubject checkFieldIsNotRenamed(
+ ClassSubject classSubject, String fieldType, String fieldName) {
+ FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
+ assertThat(fieldSubject, not(isRenamed()));
+ return fieldSubject;
+ }
+
+ protected FieldSubject checkFieldIsNotRenamed(ClassSubject classSubject, String fieldName) {
+ FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
+ assertThat(fieldSubject, not(isRenamed()));
+ return fieldSubject;
+ }
+
+ protected MethodSubject checkMethodIsRenamed(
+ ClassSubject classSubject, MethodSignature methodSignature) {
+ MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
+ assertThat(methodSubject, isRenamed());
+ return methodSubject;
+ }
+
+ protected MethodSubject checkMethodIsRenamed(ClassSubject classSubject, String methodName) {
+ MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
+ assertThat(methodSubject, isRenamed());
+ return methodSubject;
+ }
+
+ protected MethodSubject checkMethodIsNotRenamed(
+ ClassSubject classSubject, MethodSignature methodSignature) {
+ MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
+ assertThat(methodSubject, not(isRenamed()));
+ return methodSubject;
+ }
+
+ protected MethodSubject checkMethodIsNotRenamed(ClassSubject classSubject, String methodName) {
+ MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
+ assertThat(methodSubject, not(isRenamed()));
+ return methodSubject;
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
new file mode 100644
index 0000000..aa2e69d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -0,0 +1,150 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.kotlin.TestKotlinClass;
+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.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.stream.Collectors;
+import org.junit.Test;
+
+public class KotlinIntrinsicsIdentifierTest extends AbstractR8KotlinNamingTestBase {
+ private static final String FOLDER = "intrinsics_identifiers";
+
+ public KotlinIntrinsicsIdentifierTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification, boolean minification) {
+ super(targetVersion, allowAccessModification, minification);
+ }
+
+ @Test
+ public void test_example1() throws Exception {
+ TestKotlinClass ex1 = new TestKotlinClass("intrinsics_identifiers.Example1Kt");
+ String targetClassName = "ToBeRenamedClass";
+ String targetFieldName = "toBeRenamedField";
+ String targetMethodName = "toBeRenamedMethod";
+ test(ex1, targetClassName, targetFieldName, targetMethodName);
+ }
+
+ @Test
+ public void test_example2() throws Exception {
+ TestKotlinClass ex2 = new TestKotlinClass("intrinsics_identifiers.Example2Kt");
+ String targetClassName = "AnotherClass";
+ String targetFieldName = "anotherField";
+ String targetMethodName = "anotherMethod";
+ test(ex2, targetClassName, targetFieldName, targetMethodName);
+ }
+
+ @Test
+ public void test_example3() throws Exception {
+ TestKotlinClass ex3 = new TestKotlinClass("intrinsics_identifiers.Example3Kt");
+ String mainClassName = ex3.getClassName();
+ TestCompileResult result = testForR8(Backend.DEX)
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .addKeepMainRule(mainClassName)
+ .minification(minification)
+ .compile();
+ CodeInspector codeInspector = result.inspector();
+ MethodSubject main = codeInspector.clazz(ex3.getClassName()).mainMethod();
+ assertThat(main, isPresent());
+ verifyKotlinIntrinsicsRenamed(codeInspector, main);
+ }
+
+ private void verifyKotlinIntrinsicsRenamed(CodeInspector inspector, MethodSubject main) {
+ Iterator<InstructionSubject> it = main.iterateInstructions(InstructionSubject::isInvokeStatic);
+ assertTrue(it.hasNext());
+ boolean metKotlinIntrinsicsNullChecks = false;
+ while (it.hasNext()) {
+ DexMethod invokedMethod = it.next().getMethod();
+ if (invokedMethod.getHolder().toSourceString().contains("java.net")) {
+ continue;
+ }
+ ClassSubject invokedMethodHolderSubject =
+ inspector.clazz(invokedMethod.getHolder().toSourceString());
+ assertThat(invokedMethodHolderSubject, isPresent());
+ assertEquals(minification, invokedMethodHolderSubject.isRenamed());
+ MethodSubject invokedMethodSubject = invokedMethodHolderSubject.method(
+ invokedMethod.proto.returnType.toSourceString(),
+ invokedMethod.name.toString(),
+ Arrays.stream(invokedMethod.proto.parameters.values)
+ .map(DexType::toSourceString)
+ .collect(Collectors.toList()));
+ assertThat(invokedMethodSubject, isPresent());
+ assertEquals(minification, invokedMethodSubject.isRenamed());
+ if (invokedMethodSubject.getOriginalName().startsWith("check")
+ && invokedMethodSubject.getOriginalName().endsWith("Null")
+ && invokedMethodHolderSubject.getOriginalDescriptor()
+ .contains("kotlin/jvm/internal/Intrinsics")) {
+ metKotlinIntrinsicsNullChecks = true;
+ }
+ }
+ assertTrue(metKotlinIntrinsicsNullChecks);
+ }
+
+ private void test(
+ TestKotlinClass testMain,
+ String targetClassName,
+ String targetFieldName,
+ String targetMethodName) throws Exception {
+ String mainClassName = testMain.getClassName();
+ TestRunResult result = testForR8(Backend.DEX)
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .enableProguardTestOptions()
+ .addKeepMainRule(mainClassName)
+ .addKeepRules(StringUtils.lines(
+ "-neverclassinline class **." + targetClassName,
+ "-nevermerge class **." + targetClassName,
+ "-neverinline class **." + targetClassName + " { <methods>; }"
+ ))
+ .minification(minification)
+ .run(mainClassName);
+ CodeInspector codeInspector = result.inspector();
+
+ MethodSubject main = codeInspector.clazz(testMain.getClassName()).mainMethod();
+ assertThat(main, isPresent());
+ verifyKotlinIntrinsicsRenamed(codeInspector, main);
+ // Examine all const-string and verify that identifiers are not introduced.
+ Iterator<InstructionSubject> it =
+ main.iterateInstructions(i -> i.isConstString(JumboStringMode.ALLOW));
+ assertTrue(it.hasNext());
+ while (it.hasNext()) {
+ String identifier = it.next().getConstString();
+ if (identifier.contains("arg")) {
+ continue;
+ }
+ assertEquals(!minification, identifier.equals(targetMethodName));
+ assertEquals(!minification, identifier.equals(targetFieldName));
+ }
+
+ targetClassName = FOLDER + "." + targetClassName;
+ ClassSubject clazz = minification
+ ? checkClassIsRenamed(codeInspector, targetClassName)
+ : checkClassIsNotRenamed(codeInspector, targetClassName);
+ if (minification) {
+ checkFieldIsRenamed(clazz, targetFieldName);
+ checkMethodIsRenamed(clazz, targetMethodName);
+ } else {
+ checkFieldIsNotRenamed(clazz, targetFieldName);
+ checkMethodIsNotRenamed(clazz, targetMethodName);
+ }
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/InnerEnumValuesTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/InnerEnumValuesTest.java
index 6c8a9be..08d0ff3 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/InnerEnumValuesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/InnerEnumValuesTest.java
@@ -3,20 +3,43 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming.applymapping;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.naming.applymapping.Outer.InnerEnum;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
import java.nio.file.Path;
+import java.util.Collection;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+@RunWith(Parameterized.class)
public class InnerEnumValuesTest extends TestBase {
- private static Class<?> MAIN = TestApp.class;
- private static String EXPECTED_OUTPUT = StringUtils.lines("state_X", "state_Y");
+ private static final Class<?> MAIN = TestApp.class;
+ private static final String RENAMED_NAME = "x.y.z$ie";
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("STATE_A", "STATE_B");
private static Path mappingFile;
+ private final Backend backend;
+ private final boolean minification;
+
+ @Parameterized.Parameters(name = "Backend: {0} minification: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(Backend.values(), BooleanUtils.values());
+ }
+
+ public InnerEnumValuesTest(Backend backend, boolean minification) {
+ this.backend = backend;
+ this.minification = minification;
+ }
@Before
public void setup() throws Exception {
@@ -27,7 +50,7 @@
StringUtils.lines(
Outer.class.getTypeName() + " -> " + "x.y.z:",
" void <init>() -> <init>",
- InnerEnum.class.getTypeName() + " -> " + "x.y.z$ie:",
+ InnerEnum.class.getTypeName() + " -> " + RENAMED_NAME + ":",
" " + InnerEnum.class.getTypeName() + " STATE_A -> state_X",
" " + InnerEnum.class.getTypeName() + " STATE_B -> state_Y",
" " + InnerEnum.class.getTypeName() + "[] $VALUES -> XY",
@@ -37,16 +60,26 @@
" " + InnerEnum.class.getTypeName() + "[] values() -> values"));
}
- @Ignore("b/124177369")
@Test
public void b124177369() throws Exception {
- testForR8(Backend.DEX)
+ testForR8(backend)
.addProgramClassesAndInnerClasses(Outer.class)
.addProgramClasses(MAIN)
.addKeepMainRule(MAIN)
- .addKeepRules("-dontoptimize")
.addKeepRules("-applymapping " + mappingFile.toAbsolutePath())
+ .minification(minification)
.compile()
+ .inspect(inspector -> {
+ ClassSubject enumSubject = inspector.clazz(RENAMED_NAME);
+ assertThat(enumSubject, isPresent());
+ assertEquals(minification, enumSubject.isRenamed());
+ String fieldName =
+ minification
+ ? "a" // minified name
+ : "state_X"; // mapped name without minification
+ FieldSubject stateA = enumSubject.uniqueFieldWithName(fieldName);
+ assertThat(stateA, isPresent());
+ })
.run(MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
new file mode 100644
index 0000000..f599f4d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
@@ -0,0 +1,176 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.signature;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestBase;
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import org.junit.Test;
+
+public class GenericSignatureRenamingTest extends TestBase {
+
+ @Test
+ public void testJVM() throws IOException, CompilationFailedException {
+ testForJvm().addTestClasspath().run(Main.class).assertSuccess();
+ }
+
+ @Test
+ public void testR8Dex() throws IOException, CompilationFailedException {
+ test(testForR8(Backend.DEX));
+ }
+
+ @Test
+ public void testR8CompatDex() throws IOException, CompilationFailedException {
+ test(testForR8Compat(Backend.DEX));
+ }
+
+ @Test
+ public void testR8DexNoMinify() throws IOException, CompilationFailedException {
+ test(testForR8(Backend.DEX).addKeepRules("-dontobfuscate"));
+ }
+
+ @Test
+ public void testR8Cf() throws IOException, CompilationFailedException {
+ test(testForR8(Backend.CF));
+ }
+
+ @Test
+ public void testR8CfNoMinify() throws IOException, CompilationFailedException {
+ test(testForR8(Backend.CF).addKeepRules("-dontobfuscate"));
+ }
+
+ @Test
+ public void testD8() throws IOException, CompilationFailedException {
+ testForD8()
+ .addProgramClasses(Main.class)
+ .addProgramClassesAndInnerClasses(A.class, B.class, CY.class, CYY.class)
+ .setMode(CompilationMode.RELEASE)
+ .compile()
+ .assertNoMessages()
+ .run(Main.class)
+ .assertSuccess();
+ }
+
+ private void test(R8TestBuilder builder) throws IOException, CompilationFailedException {
+ builder
+ .addKeepRules("-dontoptimize")
+ .addKeepRules("-keepattributes InnerClasses,EnclosingMethod,Signature")
+ .addKeepMainRule(Main.class)
+ .addProgramClasses(Main.class)
+ .addProgramClassesAndInnerClasses(A.class, B.class, CY.class, CYY.class)
+ .setMode(CompilationMode.RELEASE)
+ .compile()
+ .assertNoMessages()
+ .run(Main.class)
+ .assertSuccess();
+ }
+}
+
+class A<T> {
+ class Y {
+
+ class YY {}
+
+ class ZZ extends YY {
+ public YY yy;
+
+ YY newYY() {
+ return new YY();
+ }
+ }
+
+ ZZ zz() {
+ return new ZZ();
+ }
+ }
+
+ class Z extends Y {}
+
+ static class S {}
+
+ Y newY() {
+ return new Y();
+ }
+
+ Z newZ() {
+ return new Z();
+ }
+
+ Y.ZZ newZZ() {
+ return new Y().zz();
+ }
+}
+
+class B<T extends A<T>> {}
+
+class CY<T extends A<T>.Y> {}
+
+class CYY<T extends A<T>.Y.YY> {}
+
+class Main {
+
+ private static void check(boolean b, String message) {
+ if (!b) {
+ throw new RuntimeException("Check failed: " + message);
+ }
+ }
+
+ public static void main(String[] args) {
+ A.Z z = new A().newZ();
+ A.Y.YY yy = new A().newZZ().yy;
+
+ B b = new B();
+ CY cy = new CY();
+
+ CYY cyy = new CYY();
+ A.S s = new A.S();
+
+ // Check if names of Z and ZZ shows A as a superclass.
+ Class classA = new A().getClass();
+ String nameA = classA.getName();
+
+ TypeVariable[] v = classA.getTypeParameters();
+ check(v != null && v.length == 1, classA + " expected to have 1 type parameter.");
+
+ Class classZ = new A().newZ().getClass();
+ String nameZ = classZ.getName();
+ check(nameZ.startsWith(nameA + "$"), nameZ + " expected to start with " + nameA + "$.");
+
+ Class classZZ = new A().newZZ().getClass();
+ String nameZZ = classZZ.getName();
+ check(nameZZ.startsWith(nameA + "$"), nameZZ + " expected to start with " + nameA + "$.");
+
+ // Check that the owner of the superclass of Z is A.
+ Class ownerClassOfSuperOfZ = getEnclosingClass(classZ.getGenericSuperclass());
+
+ check(
+ ownerClassOfSuperOfZ == A.class,
+ ownerClassOfSuperOfZ + " expected to be equal to " + A.class);
+
+ // Check that the owner-owner of the superclass of Z is A.
+ Class ownerOfownerOfSuperOfZZ =
+ getEnclosingClass(classZZ.getGenericSuperclass()).getEnclosingClass();
+
+ check(
+ ownerOfownerOfSuperOfZZ == A.class,
+ ownerOfownerOfSuperOfZZ + " expected to be equal to " + A.class);
+ }
+
+ private static Class getEnclosingClass(Type type) {
+ if (type instanceof ParameterizedType) {
+ // On the JVM it's a ParameterizedType.
+ return (Class) ((ParameterizedType) ((ParameterizedType) type).getOwnerType()).getRawType();
+ } else {
+ // On the ART it's Class.
+ check(type instanceof Class, type + " expected to be a ParameterizedType or Class.");
+ return ((Class) type).getEnclosingClass();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
index 1dffcee..dc980d7 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.smali;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Smali;
@@ -106,8 +107,8 @@
public class InterfaceBuilder extends Builder {
- InterfaceBuilder(String name, String superName) {
- super(name, superName, ImmutableList.of());
+ InterfaceBuilder(String name, String superName, List<String> implementedInterfaces) {
+ super(name, superName, implementedInterfaces);
}
public String toString() {
@@ -125,6 +126,7 @@
private String currentClassName;
private final Map<String, Builder> classes = new HashMap<>();
+ private AndroidApiLevel minApi = AndroidApiLevel.I_MR1;
public SmaliBuilder() {
// No default class.
@@ -138,6 +140,10 @@
addClass(name, superName);
}
+ public void setMinApi(AndroidApiLevel minApi) {
+ this.minApi = minApi;
+ }
+
private List<String> getSource(String clazz) {
return classes.get(clazz).source;
}
@@ -169,9 +175,13 @@
}
public void addInterface(String name, String superName) {
+ addInterface(name, superName, ImmutableList.of());
+ }
+
+ public void addInterface(String name, String superName, List<String> implementedInterfaces) {
assert !classes.containsKey(name);
currentClassName = name;
- classes.put(name, new InterfaceBuilder(name, superName));
+ classes.put(name, new InterfaceBuilder(name, superName, implementedInterfaces));
}
public void setSourceFile(String file) {
@@ -360,7 +370,7 @@
}
public byte[] compile() throws IOException, RecognitionException, ExecutionException {
- return Smali.compile(buildSource());
+ return Smali.compile(buildSource(), minApi.getLevel());
}
public AndroidApp build() throws IOException, RecognitionException, ExecutionException {
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index b99cbdc..3e3fd4e 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -105,6 +105,7 @@
SingleFileConsumer consumer = new SingleFileConsumer();
AndroidApp app = AndroidApp.builder().addDexProgramData(data, Origin.unknown()).build();
InternalOptions options = new InternalOptions();
+ options.minApiLevel = apiLevel;
options.programConsumer = consumer;
ExecutorService executor = ThreadUtils.getExecutorService(1);
try {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 93a1c37..5f1b405 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -142,6 +142,14 @@
}
@Override
+ public String getConstString() {
+ if (instruction instanceof CfConstString) {
+ return ((CfConstString) instruction).getString().toSourceString();
+ }
+ return null;
+ }
+
+ @Override
public boolean isConstClass() {
return instruction instanceof CfConstClass;
}
@@ -193,6 +201,12 @@
}
@Override
+ public boolean isNewInstance(String type) {
+ return isNewInstance()
+ && ((CfNew) instruction).getType().toString().equals(type);
+ }
+
+ @Override
public boolean isCheckCast() {
return instruction instanceof CfCheckCast;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
index 2d7aec8..663a070 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
@@ -58,7 +58,7 @@
@Override
public boolean hasCatchAll() {
- return isCatching(DexItemFactory.catchAllType.toDescriptorString());
+ return isCatching(DexItemFactory.throwableDescriptorString);
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index c9b7ee5..ba23a0a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -252,6 +252,17 @@
}
@Override
+ public String getConstString() {
+ if (instruction instanceof ConstString) {
+ return ((ConstString) instruction).BBBB.toSourceString();
+ }
+ if (instruction instanceof ConstStringJumbo) {
+ return ((ConstStringJumbo) instruction).BBBBBBBB.toSourceString();
+ }
+ return null;
+ }
+
+ @Override
public boolean isConstClass() {
return instruction instanceof ConstClass;
}
@@ -303,6 +314,12 @@
}
@Override
+ public boolean isNewInstance(String type) {
+ return isNewInstance()
+ && ((NewInstance) instruction).getType().toString().equals(type);
+ }
+
+ @Override
public boolean isCheckCast() {
return instruction instanceof CheckCast;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 0528185..f2eb753 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -48,6 +48,8 @@
boolean isConstString(String value, JumboStringMode jumboStringMode);
+ String getConstString();
+
boolean isConstClass();
boolean isConstClass(String type);
@@ -68,6 +70,8 @@
boolean isNewInstance();
+ boolean isNewInstance(String type);
+
boolean isCheckCast();
boolean isCheckCast(String type);
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/ToBeRenamedClass.java b/src/test/kotlinR8TestResources/intrinsics_identifiers/ToBeRenamedClass.java
new file mode 100644
index 0000000..6218d16
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/ToBeRenamedClass.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package intrinsics_identifiers;
+
+class ToBeRenamedClass {
+ String toBeRenamedField = "SUFFIX";
+ String toBeRenamedMethod(String arg) {
+ return arg + toBeRenamedField;
+ }
+ void updateField(String arg) {
+ toBeRenamedField = arg;
+ }
+}
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/example1.kt b/src/test/kotlinR8TestResources/intrinsics_identifiers/example1.kt
new file mode 100644
index 0000000..4409232
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/example1.kt
@@ -0,0 +1,17 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package intrinsics_identifiers
+
+fun main(args: Array<String>) {
+ val instance = ToBeRenamedClass()
+ println(instance.toBeRenamedField)
+ println(instance.toBeRenamedMethod("arg1"))
+
+ if (instance.toBeRenamedField.equals("arg2")) {
+ instance.updateField("arg3")
+ println(instance.toBeRenamedField)
+ println(instance.toBeRenamedMethod("arg4"))
+ }
+}
+
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/example2.kt b/src/test/kotlinR8TestResources/intrinsics_identifiers/example2.kt
new file mode 100644
index 0000000..c6c5e09
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/example2.kt
@@ -0,0 +1,27 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package intrinsics_identifiers
+
+class AnotherClass {
+ var anotherField : String = "PREFIX"
+ fun anotherMethod(arg: String) : String {
+ return anotherField + arg
+ }
+ fun updateField(arg: String) : Unit {
+ anotherField = arg
+ }
+}
+
+fun main(args: Array<String>) {
+ val instance = AnotherClass()
+ println(instance.anotherField)
+ println(instance.anotherMethod("arg1"))
+
+ if (instance.anotherField.equals("arg2")) {
+ instance.updateField("arg3")
+ println(instance.anotherField)
+ println(instance.anotherMethod("arg4"))
+ }
+}
+
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/example3.kt b/src/test/kotlinR8TestResources/intrinsics_identifiers/example3.kt
new file mode 100644
index 0000000..47b1d00
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/example3.kt
@@ -0,0 +1,14 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package intrinsics_identifiers
+
+import java.net.URI
+
+fun main(args: Array<String>) {
+ // By specifying non-null type of variables for library uses,
+ // kotlin.jvm.internal.Intrinsics#check*Null(...) is added by kotlinc.
+ val uri : URI = URI.create("google.com")
+ val host : String = uri.host
+ println(host)
+}
\ No newline at end of file
diff --git a/tools/archive.py b/tools/archive.py
index 5c0c231..687844a 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -8,6 +8,7 @@
import jdk
import optparse
import os
+import resource
import shutil
import subprocess
import sys
@@ -90,16 +91,21 @@
def GetMavenUrl(is_master):
return GetVersionDestination('http://storage.googleapis.com/', '', is_master)
+def PrintResourceInfo():
+ (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
+ print('INFO: Open files soft limit: %s' % soft)
+ print('INFO: Open files hard limit: %s' % hard)
+
def Main():
(options, args) = ParseOptions()
- # TODO(126871526): Fix the is_bot check.
- # if not utils.is_bot() and not options.dry_run:
- # raise Exception('You are not a bot, don\'t archive builds')
+ if not utils.is_bot() and not options.dry_run:
+ raise Exception('You are not a bot, don\'t archive builds')
if utils.is_old_bot():
print("Archiving is disabled on old bots.")
return
+ PrintResourceInfo()
# Create maven release which uses a build that exclude dependencies.
create_maven_release.main(["--out", utils.LIBS])
diff --git a/tools/as_utils.py b/tools/as_utils.py
index 363e1b9..aa160a1 100644
--- a/tools/as_utils.py
+++ b/tools/as_utils.py
@@ -66,14 +66,12 @@
if ('/r8.jar' not in line) and ('/r8lib.jar' not in line):
f.write(line)
-def GetMinAndCompileSdk(app, config, checkout_dir, apk_reference):
-
- compile_sdk = config.get('compile_sdk', None)
- min_sdk = config.get('min_sdk', None)
+def GetMinAndCompileSdk(app, checkout_dir, apk_reference):
+ compile_sdk = app.compile_sdk
+ min_sdk = app.min_sdk
if not compile_sdk or not min_sdk:
- app_module = config.get('app_module', 'app')
- build_gradle_file = os.path.join(checkout_dir, app_module, 'build.gradle')
+ build_gradle_file = os.path.join(checkout_dir, app.module, 'build.gradle')
assert os.path.isfile(build_gradle_file), (
'Expected to find build.gradle file at {}'.format(build_gradle_file))
@@ -82,11 +80,11 @@
for line in f.readlines():
stripped = line.strip()
if stripped.startswith('compileSdkVersion '):
- if 'compile_sdk' not in config:
+ if not app.compile_sdk:
assert not compile_sdk
compile_sdk = int(stripped[len('compileSdkVersion '):])
elif stripped.startswith('minSdkVersion '):
- if 'min_sdk' not in config:
+ if not app.min_sdk:
assert not min_sdk
min_sdk = int(stripped[len('minSdkVersion '):])
@@ -123,9 +121,8 @@
or 'transformClassesWithDexBuilderFor' in x
or 'transformDexArchiveWithDexMergerFor' in x)
-def SetPrintConfigurationDirective(app, config, checkout_dir, destination):
- proguard_config_file = FindProguardConfigurationFile(
- app, config, checkout_dir)
+def SetPrintConfigurationDirective(app, checkout_dir, destination):
+ proguard_config_file = FindProguardConfigurationFile(app, checkout_dir)
with open(proguard_config_file) as f:
lines = f.readlines()
with open(proguard_config_file, 'w') as f:
@@ -137,11 +134,10 @@
f.write('\n')
f.write('-printconfiguration {}\n'.format(destination))
-def FindProguardConfigurationFile(app, config, checkout_dir):
- app_module = config.get('app_module', 'app')
+def FindProguardConfigurationFile(app, checkout_dir):
candidates = ['proguard-rules.pro', 'proguard-rules.txt', 'proguard.cfg']
for candidate in candidates:
- proguard_config_file = os.path.join(checkout_dir, app_module, candidate)
+ proguard_config_file = os.path.join(checkout_dir, app.module, candidate)
if os.path.isfile(proguard_config_file):
return proguard_config_file
# Currently assuming that the Proguard configuration file can be found at
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index d95453f..5eabd7f 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -29,141 +29,306 @@
and os.path.isdir(os.environ['R8_BENCHMARK_DIR'])):
WORKING_DIR = os.environ['R8_BENCHMARK_DIR']
-# For running on Golem all APPS are bundled as an x20-dependency and then copied
-# to WORKING_DIR. To update the app-bundle use 'run_on_as_app_x20_packager.py'.
-APPS = {
- # 'app-name': {
- # 'git_repo': ...
+class Repo(object):
+ def __init__(self, fields):
+ self.__dict__ = fields
+
+ # If there is only one app in this repository, then give the app the same
+ # name as the repository, if it does not already have one.
+ if len(self.apps) == 1:
+ app = self.apps[0]
+ if not app.name:
+ app.name = self.name
+
+class App(object):
+ def __init__(self, fields):
+ module = fields.get('module', 'app')
+ defaults = {
+ 'archives_base_name': module,
+ 'build_dir': 'build',
+ 'compile_sdk': None,
+ 'dir': '.',
+ 'flavor': None,
+ 'main_dex_rules': None,
+ 'module': module,
+ 'min_sdk': None,
+ 'name': None,
+ 'releaseTarget': None,
+ 'signed_apk_name': None,
+ 'skip': False
+ }
+ self.__dict__ = dict(defaults.items() + fields.items())
+
+# For running on Golem all third-party repositories are bundled as an x20-
+# dependency and then copied to WORKING_DIR. To update the app-bundle use
+# 'run_on_as_app_x20_packager.py'.
+APP_REPOSITORIES = [
+ # ...
+ # Repo({
+ # 'name': ...,
+ # 'url': ...,
# 'revision': ...,
- # 'app_module': ... (default app)
- # 'archives_base_name': ... (default same as app_module)
- # 'flavor': ... (default no flavor)
- # 'releaseTarget': ... (default <app_module>:assemble<flavor>Release
- # },
- 'AnExplorer': {
- 'app_id': 'dev.dworks.apps.anexplorer.pro',
- 'git_repo': 'https://github.com/christofferqa/AnExplorer',
+ # 'apps': [
+ # {
+ # 'id': ...,
+ # 'dir': ...,
+ # 'module': ... (default app)
+ # 'name': ...,
+ # 'archives_base_name': ... (default same as module)
+ # 'flavor': ... (default no flavor)
+ # 'releaseTarget': ... (default <module>:assemble<flavor>Release
+ # },
+ # ...
+ # ]
+ # }),
+ # ...
+ Repo({
+ 'name': 'android-suite',
+ 'url': 'https://github.com/christofferqa/android-suite',
+ 'revision': '46c96f214711cf6cdcb72cc0c94520ef418e3739',
+ 'apps': [
+ App({
+ 'id': 'com.numix.calculator',
+ 'dir': 'Calculator',
+ 'name': 'numix-calculator'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'AnExplorer',
+ 'url': 'https://github.com/christofferqa/AnExplorer',
'revision': '365927477b8eab4052a1882d5e358057ae3dee4d',
- 'flavor': 'googleMobilePro',
- 'signed-apk-name': 'AnExplorer-googleMobileProRelease-4.0.3.apk',
- 'min_sdk': 17
- },
- 'AntennaPod': {
- 'app_id': 'de.danoeh.antennapod',
- 'git_repo': 'https://github.com/christofferqa/AntennaPod.git',
+ 'apps': [
+ App({
+ 'id': 'dev.dworks.apps.anexplorer.pro',
+ 'flavor': 'googleMobilePro',
+ 'signed_apk_name': 'AnExplorer-googleMobileProRelease-4.0.3.apk',
+ 'min_sdk': 17
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'AntennaPod',
+ 'url': 'https://github.com/christofferqa/AntennaPod.git',
'revision': '77e94f4783a16abe9cc5b78dc2d2b2b1867d8c06',
- 'flavor': 'play',
- 'min_sdk': 14,
- 'compile_sdk': 26
- },
- 'apps-android-wikipedia': {
- 'app_id': 'org.wikipedia',
- 'git_repo': 'https://github.com/christofferqa/apps-android-wikipedia',
+ 'apps': [
+ App({
+ 'id': 'de.danoeh.antennapod',
+ 'flavor': 'play',
+ 'min_sdk': 14,
+ 'compile_sdk': 26
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'apps-android-wikipedia',
+ 'url': 'https://github.com/christofferqa/apps-android-wikipedia',
'revision': '686e8aa5682af8e6a905054b935dd2daa57e63ee',
- 'flavor': 'prod',
- 'signed-apk-name': 'app-prod-universal-release.apk',
- },
- 'chanu': {
- 'app_id': 'com.chanapps.four.activity',
- 'git_repo': 'https://github.com/mkj-gram/chanu.git',
+ 'apps': [
+ App({
+ 'id': 'org.wikipedia',
+ 'flavor': 'prod',
+ 'signed_apk_name': 'app-prod-universal-release.apk'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'chanu',
+ 'url': 'https://github.com/mkj-gram/chanu.git',
'revision': '04ade1e9c33d707f0850d5eb9d6fa5e8af814a26',
- },
- 'friendlyeats-android': {
- 'app_id': 'com.google.firebase.example.fireeats',
- 'git_repo': 'https://github.com/christofferqa/friendlyeats-android.git',
+ 'apps': [
+ App({
+ 'id': 'com.chanapps.four.activity'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'friendlyeats-android',
+ 'url': 'https://github.com/christofferqa/friendlyeats-android.git',
'revision': '10091fa0ec37da12e66286559ad1b6098976b07b',
- },
- 'Instabug-Android': {
- 'app_id': 'com.example.instabug',
- 'git_repo': 'https://github.com/christofferqa/Instabug-Android.git',
- 'revision': 'b8df78c96630a6537fbc07787b4990afc030cc0f'
- },
- 'KISS': {
- 'app_id': 'fr.neamar.kiss',
- 'git_repo': 'https://github.com/christofferqa/KISS',
+ 'apps': [
+ App({
+ 'id': 'com.google.firebase.example.fireeats'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'Instabug-Android',
+ 'url': 'https://github.com/christofferqa/Instabug-Android.git',
+ 'revision': 'b8df78c96630a6537fbc07787b4990afc030cc0f',
+ 'apps': [
+ App({
+ 'id': 'com.example.instabug'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'KISS',
+ 'url': 'https://github.com/christofferqa/KISS',
'revision': '093da9ee0512e67192f62951c45a07a616fc3224',
- },
- 'materialistic': {
- 'app_id': 'io.github.hidroh.materialistic',
- 'git_repo': 'https://github.com/christofferqa/materialistic',
+ 'apps': [
+ App({
+ 'id': 'fr.neamar.kiss'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'materialistic',
+ 'url': 'https://github.com/christofferqa/materialistic',
'revision': '2b2b2ee25ce9e672d5aab1dc90a354af1522b1d9',
- },
- 'Minimal-Todo': {
- 'app_id': 'com.avjindersinghsekhon.minimaltodo',
- 'git_repo': 'https://github.com/christofferqa/Minimal-Todo',
+ 'apps': [
+ App({
+ 'id': 'io.github.hidroh.materialistic'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'Minimal-Todo',
+ 'url': 'https://github.com/christofferqa/Minimal-Todo',
'revision': '9d8c73746762cd376b718858ec1e8783ca07ba7c',
- },
- 'NewPipe': {
- 'app_id': 'org.schabi.newpipe',
- 'git_repo': 'https://github.com/christofferqa/NewPipe',
+ 'apps': [
+ App({
+ 'id': 'com.avjindersinghsekhon.minimaltodo'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'NewPipe',
+ 'url': 'https://github.com/christofferqa/NewPipe',
'revision': 'ed543099c7823be00f15d9340f94bdb7cb37d1e6',
- },
- 'rover-android': {
- 'app_id': 'io.rover.app.debug',
- 'app_module': 'debug-app',
- 'git_repo': 'https://github.com/mkj-gram/rover-android.git',
+ 'apps': [
+ App({
+ 'id': 'org.schabi.newpipe'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'rover-android',
+ 'url': 'https://github.com/mkj-gram/rover-android.git',
'revision': '859af82ba56fe9035ae9949156c7a88e6012d930',
- },
- 'Signal-Android': {
- 'app_id': 'org.thoughtcrime.securesms',
- 'app_module': '',
- 'flavor': 'play',
- 'git_repo': 'https://github.com/mkj-gram/Signal-Android.git',
- 'main_dex_rules': 'multidex-config.pro',
+ 'apps': [
+ App({
+ 'id': 'io.rover.app.debug',
+ 'module': 'debug-app'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'Signal-Android',
+ 'url': 'https://github.com/mkj-gram/Signal-Android.git',
'revision': 'a45d0c1fed20fa39e8b9445fe7790326f46b3166',
- 'releaseTarget': 'assemblePlayRelease',
- 'signed-apk-name': 'Signal-play-release-4.32.7.apk',
- },
- 'Simple-Calendar': {
- 'app_id': 'com.simplemobiletools.calendar.pro',
- 'git_repo': 'https://github.com/christofferqa/Simple-Calendar',
+ 'apps': [
+ App({
+ 'id': 'org.thoughtcrime.securesms',
+ 'module': '',
+ 'flavor': 'play',
+ 'main_dex_rules': 'multidex-config.pro',
+ 'releaseTarget': 'assemblePlayRelease',
+ 'signed_apk_name': 'Signal-play-release-4.32.7.apk'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'Simple-Calendar',
+ 'url': 'https://github.com/christofferqa/Simple-Calendar',
'revision': '82dad8c203eea5a0f0ddb513506d8f1de986ef2b',
- 'signed-apk-name': 'calendar-release.apk'
- },
- 'sqldelight': {
- 'app_id': 'com.example.sqldelight.hockey',
- 'git_repo': 'https://github.com/christofferqa/sqldelight.git',
+ 'apps': [
+ App({
+ 'id': 'com.simplemobiletools.calendar.pro',
+ 'signed_apk_name': 'calendar-release.apk'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'sqldelight',
+ 'url': 'https://github.com/christofferqa/sqldelight.git',
'revision': '2e67a1126b6df05e4119d1e3a432fde51d76cdc8',
- 'app_module': 'sample/android',
- 'archives_base_name': 'android',
- 'min_sdk': 14,
- 'compile_sdk': 28,
- },
- 'tachiyomi': {
- 'app_id': 'eu.kanade.tachiyomi',
- 'git_repo': 'https://github.com/sgjesse/tachiyomi.git',
+ 'apps': [
+ App({
+ 'id': 'com.example.sqldelight.hockey',
+ 'module': 'sample/android',
+ 'archives_base_name': 'android',
+ 'min_sdk': 14,
+ 'compile_sdk': 28
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'tachiyomi',
+ 'url': 'https://github.com/sgjesse/tachiyomi.git',
'revision': 'b15d2fe16864645055af6a745a62cc5566629798',
- 'flavor': 'standard',
- 'releaseTarget': 'app:assembleRelease',
- 'min_sdk': 16
- },
- 'tivi': {
- 'app_id': 'app.tivi',
- 'git_repo': 'https://github.com/sgjesse/tivi.git',
+ 'apps': [
+ App({
+ 'id': 'eu.kanade.tachiyomi',
+ 'flavor': 'standard',
+ 'releaseTarget': 'app:assembleRelease',
+ 'min_sdk': 16
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'tivi',
+ 'url': 'https://github.com/sgjesse/tivi.git',
'revision': '25c52e3593e7c98da4e537b49b29f6f67f88754d',
- 'min_sdk': 23,
- 'compile_sdk': 28,
- },
- 'Tusky': {
- 'app_id': 'com.keylesspalace.tusky',
- 'git_repo': 'https://github.com/mkj-gram/Tusky.git',
+ 'apps': [
+ App({
+ 'id': 'app.tivi',
+ 'min_sdk': 23,
+ 'compile_sdk': 28
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'Tusky',
+ 'url': 'https://github.com/mkj-gram/Tusky.git',
'revision': 'b794f3ab90388add98461ffe70edb65c39351c33',
- 'flavor': 'blue'
- },
- 'Vungle-Android-SDK': {
- 'app_id': 'com.publisher.vungle.sample',
- 'git_repo': 'https://github.com/mkj-gram/Vungle-Android-SDK.git',
+ 'apps': [
+ App({
+ 'id': 'com.keylesspalace.tusky',
+ 'flavor': 'blue'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'Vungle-Android-SDK',
+ 'url': 'https://github.com/mkj-gram/Vungle-Android-SDK.git',
'revision': '3e231396ea7ce97b2655e03607497c75730e45f6',
- },
+ 'apps': [
+ App({
+ 'id': 'com.publisher.vungle.sample'
+ })
+ ]
+ }),
# This does not build yet.
- 'muzei': {
- 'git_repo': 'https://github.com/sgjesse/muzei.git',
+ Repo({
+ 'name': 'muzei',
+ 'url': 'https://github.com/sgjesse/muzei.git',
'revision': 'bed2a5f79c6e08b0a21e3e3f9242232d0848ef74',
- 'app_module': 'main',
- 'archives_base_name': 'muzei',
- 'skip': True,
- },
-}
+ 'apps': [
+ App({
+ 'module': 'main',
+ 'archives_base_name': 'muzei',
+ 'skip': True
+ })
+ ]
+ })
+]
+
+def GetAllApps():
+ apps = []
+ for repo in APP_REPOSITORIES:
+ for app in repo.apps:
+ apps.append((app, repo))
+ return apps
+
+def GetAllAppNames():
+ return [app.name for (app, repo) in GetAllApps()]
+
+def GetAppWithName(query):
+ for (app, repo) in GetAllApps():
+ if app.name == query:
+ return (app, repo)
+ assert False
# TODO(christofferqa): Do not rely on 'emulator-5554' name
emulator_id = 'emulator-5554'
@@ -221,15 +386,15 @@
def IsTrackedByGit(file):
return subprocess.check_output(['git', 'ls-files', file]).strip() != ''
-def GitClone(git_url, revision, checkout_dir, quiet):
+def GitClone(repo, checkout_dir, quiet):
result = subprocess.check_output(
- ['git', 'clone', git_url, checkout_dir]).strip()
+ ['git', 'clone', repo.url, checkout_dir]).strip()
head_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
- if revision == head_rev:
+ if repo.revision == head_rev:
return result
warn('Target revision is not head in {}.'.format(checkout_dir))
with utils.ChangedWorkingDirectory(checkout_dir, quiet=quiet):
- subprocess.check_output(['git', 'reset', '--hard', revision])
+ subprocess.check_output(['git', 'reset', '--hard', repo.revision])
return result
def GitCheckout(file):
@@ -249,10 +414,9 @@
else:
return '+' + str(round((after - before) / before * 100)) + '%'
-def UninstallApkOnEmulator(app, config, options):
- app_id = config.get('app_id')
+def UninstallApkOnEmulator(app, options):
process = subprocess.Popen(
- ['adb', '-s', emulator_id, 'uninstall', app_id],
+ ['adb', '-s', emulator_id, 'uninstall', app.id],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
@@ -260,13 +424,13 @@
# Successfully uninstalled
return
- if 'Unknown package: {}'.format(app_id) in stderr:
+ if 'Unknown package: {}'.format(app.id) in stderr:
# Application not installed
return
raise Exception(
'Unexpected result from `adb uninstall {}\nStdout: {}\nStderr: {}'.format(
- app_id, stdout, stderr))
+ app.id, stdout, stderr))
def WaitForEmulator():
stdout = subprocess.check_output(['adb', 'devices'])
@@ -288,21 +452,21 @@
else:
return True
-def GetResultsForApp(app, config, options, temp_dir):
+def GetResultsForApp(app, repo, options, temp_dir):
# Checkout and build in the build directory.
- checkout_dir = os.path.join(WORKING_DIR, app)
+ repo_name = repo.name
+ repo_checkout_dir = os.path.join(WORKING_DIR, repo_name)
result = {}
- if not os.path.exists(checkout_dir) and not options.golem:
+ if not os.path.exists(repo_checkout_dir) and not options.golem:
with utils.ChangedWorkingDirectory(WORKING_DIR, quiet=options.quiet):
- GitClone(
- config['git_repo'], config['revision'], checkout_dir, options.quiet)
+ GitClone(repo, repo_checkout_dir, options.quiet)
- checkout_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
- if config['revision'] != checkout_rev:
+ checkout_rev = utils.get_HEAD_sha1_for_checkout(repo_checkout_dir)
+ if repo.revision != checkout_rev:
msg = 'Checkout is not target revision for {} in {}.'.format(
- app, checkout_dir)
+ app.name, repo_checkout_dir)
if options.ignore_versions:
warn(msg)
else:
@@ -310,14 +474,16 @@
result['status'] = 'success'
+ app_checkout_dir = os.path.join(repo_checkout_dir, app.dir)
result_per_shrinker = BuildAppWithSelectedShrinkers(
- app, config, options, checkout_dir, temp_dir)
+ app, repo, options, app_checkout_dir, temp_dir)
for shrinker, shrinker_result in result_per_shrinker.iteritems():
result[shrinker] = shrinker_result
return result
-def BuildAppWithSelectedShrinkers(app, config, options, checkout_dir, temp_dir):
+def BuildAppWithSelectedShrinkers(
+ app, repo, options, checkout_dir, temp_dir):
result_per_shrinker = {}
with utils.ChangedWorkingDirectory(checkout_dir, quiet=options.quiet):
@@ -328,8 +494,9 @@
try:
out_dir = os.path.join(checkout_dir, 'out', shrinker)
(apk_dest, profile_dest_dir, proguard_config_file) = \
- BuildAppWithShrinker(app, config, shrinker, checkout_dir, out_dir,
- temp_dir, options)
+ BuildAppWithShrinker(
+ app, repo, shrinker, checkout_dir, out_dir, temp_dir,
+ options)
dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
result['apk_dest'] = apk_dest
result['build_status'] = 'success'
@@ -349,7 +516,7 @@
if result.get('build_status') == 'success':
if options.monkey:
result['monkey_status'] = 'success' if RunMonkey(
- app, config, options, apk_dest) else 'failed'
+ app, options, apk_dest) else 'failed'
if 'r8' in shrinker and options.r8_compilation_steps > 1:
recompilation_results = []
@@ -358,7 +525,8 @@
# true.
out_dir = os.path.join(checkout_dir, 'out', shrinker + '-1')
(apk_dest, profile_dest_dir, ext_proguard_config_file) = \
- BuildAppWithShrinker(app, config, shrinker, checkout_dir, out_dir,
+ BuildAppWithShrinker(
+ app, repo, shrinker, checkout_dir, out_dir,
temp_dir, options, keepRuleSynthesisForRecompilation=True)
dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
recompilation_result = {
@@ -380,17 +548,16 @@
if line.strip() and '-printconfiguration' not in line))
# Extract min-sdk and target-sdk
- (min_sdk, compile_sdk) = as_utils.GetMinAndCompileSdk(app, config,
- checkout_dir, apk_dest)
+ (min_sdk, compile_sdk) = \
+ as_utils.GetMinAndCompileSdk(app, checkout_dir, apk_dest)
# Now rebuild generated apk.
previous_apk = apk_dest
# We may need main dex rules when re-compiling with R8 as standalone.
main_dex_rules = None
- if config.get('main_dex_rules'):
- main_dex_rules = os.path.join(
- checkout_dir, config.get('main_dex_rules'))
+ if app.main_dex_rules:
+ main_dex_rules = os.path.join(checkout_dir, app.main_dex_rules)
for i in range(1, options.r8_compilation_steps):
try:
@@ -407,18 +574,19 @@
}
if options.monkey:
recompilation_result['monkey_status'] = 'success' if RunMonkey(
- app, config, options, recompiled_apk_dest) else 'failed'
+ app, options, recompiled_apk_dest) else 'failed'
recompilation_results.append(recompilation_result)
previous_apk = recompiled_apk_dest
except Exception as e:
- warn('Failed to recompile {} with {}'.format(app, shrinker))
+ warn('Failed to recompile {} with {}'.format(
+ app.name, shrinker))
recompilation_results.append({ 'build_status': 'failed' })
break
result['recompilation_results'] = recompilation_results
result_per_shrinker[shrinker] = result
- if not options.app:
+ if len(options.apps) > 1:
print('')
LogResultsForApp(app, result_per_shrinker, options)
print('')
@@ -426,10 +594,10 @@
return result_per_shrinker
def BuildAppWithShrinker(
- app, config, shrinker, checkout_dir, out_dir, temp_dir, options,
+ app, repo, shrinker, checkout_dir, out_dir, temp_dir, options,
keepRuleSynthesisForRecompilation=False):
print('Building {} with {}{}'.format(
- app,
+ app.name,
shrinker,
' for recompilation' if keepRuleSynthesisForRecompilation else ''))
@@ -441,9 +609,7 @@
# Add 'r8.jar' to top-level build.gradle.
as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))
- app_module = config.get('app_module', 'app')
- archives_base_name = config.get('archives_base_name', app_module)
- flavor = config.get('flavor')
+ archives_base_name = app.archives_base_name
if not os.path.exists(out_dir):
os.makedirs(out_dir)
@@ -452,16 +618,16 @@
proguard_config_dest = os.path.abspath(
os.path.join(out_dir, 'proguard-rules.pro'))
as_utils.SetPrintConfigurationDirective(
- app, config, checkout_dir, proguard_config_dest)
+ app, checkout_dir, proguard_config_dest)
env = {}
env['ANDROID_HOME'] = utils.getAndroidHome()
env['JAVA_OPTS'] = '-ea:com.android.tools.r8...'
- releaseTarget = config.get('releaseTarget')
+ releaseTarget = app.releaseTarget
if not releaseTarget:
- releaseTarget = app_module.replace('/', ':') + ':' + 'assemble' + (
- flavor.capitalize() if flavor else '') + 'Release'
+ releaseTarget = app.module.replace('/', ':') + ':' + 'assemble' + (
+ app.flavor.capitalize() if app.flavor else '') + 'Release'
# Value for property android.enableR8.
enableR8 = 'r8' in shrinker
@@ -480,14 +646,17 @@
stdout = utils.RunCmd(cmd, env, quiet=options.quiet)
apk_base_name = (archives_base_name
- + (('-' + flavor) if flavor else '') + '-release')
- signed_apk_name = config.get('signed-apk-name', apk_base_name + '.apk')
+ + (('-' + app.flavor) if app.flavor else '') + '-release')
+ signed_apk_name = (
+ app.signed_apk_name
+ if app.signed_apk_name
+ else apk_base_name + '.apk')
unsigned_apk_name = apk_base_name + '-unsigned.apk'
- build_dir = config.get('build_dir', 'build')
- build_output_apks = os.path.join(app_module, build_dir, 'outputs', 'apk')
- if flavor:
- build_output_apks = os.path.join(build_output_apks, flavor, 'release')
+ build_dir = app.build_dir
+ build_output_apks = os.path.join(app.module, build_dir, 'outputs', 'apk')
+ if app.flavor:
+ build_output_apks = os.path.join(build_output_apks, app.flavor, 'release')
else:
build_output_apks = os.path.join(build_output_apks, 'release')
@@ -525,7 +694,7 @@
assert 'r8' in shrinker
assert apk_dest.endswith('.apk')
- print('Rebuilding {} with {}'.format(app, shrinker))
+ print('Rebuilding {} with {}'.format(app.name, shrinker))
# Compile given APK with shrinker to temporary zip file.
android_jar = utils.get_android_jar(compile_sdk)
@@ -557,21 +726,20 @@
apk, dex=zip_dest, resources='META-INF/services/*', out=apk_dest,
quiet=options.quiet)
-def RunMonkey(app, config, options, apk_dest):
+def RunMonkey(app, options, apk_dest):
if not WaitForEmulator():
return False
- UninstallApkOnEmulator(app, config, options)
+ UninstallApkOnEmulator(app, options)
InstallApkOnEmulator(apk_dest, options)
- app_id = config.get('app_id')
number_of_events_to_generate = options.monkey_events
# Intentionally using a constant seed such that the monkey generates the same
# event sequence for each shrinker.
random_seed = 42
- cmd = ['adb', 'shell', 'monkey', '-p', app_id, '-s', str(random_seed),
+ cmd = ['adb', 'shell', 'monkey', '-p', app.id, '-s', str(random_seed),
str(number_of_events_to_generate)]
try:
@@ -581,7 +749,7 @@
except subprocess.CalledProcessError as e:
succeeded = False
- UninstallApkOnEmulator(app, config, options)
+ UninstallApkOnEmulator(app, options)
return succeeded
@@ -610,7 +778,7 @@
def LogComparisonResultsForApp(app, result_per_shrinker, options):
- print(app + ':')
+ print(app.name + ':')
if result_per_shrinker.get('status', 'success') != 'success':
error_message = result_per_shrinker.get('error_message')
@@ -685,7 +853,7 @@
result = optparse.OptionParser()
result.add_option('--app',
help='What app to run on',
- choices=APPS.keys())
+ choices=GetAllAppNames())
result.add_option('--download-only', '--download_only',
help='Whether to download apps without any compilation',
default=False,
@@ -742,6 +910,11 @@
result.add_option('--version',
help='The version of R8 to use (e.g., 1.4.51)')
(options, args) = result.parse_args(argv)
+ if options.app:
+ options.apps = [GetAppWithName(options.app)]
+ del options.app
+ else:
+ options.apps = GetAllApps()
if options.shrinker:
for shrinker in options.shrinker:
assert shrinker in SHRINKERS
@@ -760,13 +933,13 @@
options.shrinker.remove('r8-nolib-full')
return (options, args)
-def download_apps(quiet):
- # Download apps and place in build
+def clone_repositories(quiet):
+ # Clone repositories into WORKING_DIR.
with utils.ChangedWorkingDirectory(WORKING_DIR):
- for app, config in APPS.iteritems():
- app_dir = os.path.join(WORKING_DIR, app)
- if not os.path.exists(app_dir):
- GitClone(config['git_repo'], config['revision'], app_dir, quiet)
+ for name, repo in APP_REPOSITORIES.iteritems():
+ repo_dir = os.path.join(WORKING_DIR, name)
+ if not os.path.exists(repo_dir):
+ GitClone(repo, repo_dir, quiet)
def main(argv):
@@ -785,7 +958,7 @@
os.makedirs(WORKING_DIR)
if options.download_only:
- download_apps(options.quiet)
+ clone_repositories(options.quiet)
return
with utils.TempDir() as temp_dir:
@@ -813,14 +986,11 @@
result_per_shrinker_per_app = {}
- if options.app:
- result_per_shrinker_per_app[options.app] = GetResultsForApp(
- options.app, APPS.get(options.app), options, temp_dir)
- else:
- for app, config in sorted(APPS.iteritems(), key=lambda s: s[0].lower()):
- if not config.get('skip', False):
- result_per_shrinker_per_app[app] = GetResultsForApp(
- app, config, options, temp_dir)
+ for (app, repo) in options.apps:
+ if app.skip:
+ continue
+ result_per_shrinker_per_app[app.name] = \
+ GetResultsForApp(app, repo, options, temp_dir)
LogResultsForApps(result_per_shrinker_per_app, options)