Merge "Revert "Disable bot check when archiving""
diff --git a/build.gradle b/build.gradle
index 6afc31a..b6b1772 100644
--- a/build.gradle
+++ b/build.gradle
@@ -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 4755dca..8069844 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -63,6 +63,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -275,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(
@@ -351,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();
@@ -530,6 +539,9 @@
new SourceFileRewriter(appView.appInfo(), options).run();
timing.end();
+ // Collect the already pruned types before creating a new app info without liveness.
+ Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
+
if (!options.mainDexKeepRules.isEmpty()) {
appView.setAppInfo(new AppInfoWithSubtyping(application));
// No need to build a new main dex root set
@@ -593,7 +605,9 @@
appViewWithLiveness.setAppInfo(
appViewWithLiveness
.appInfo()
- .prunedCopyFrom(application, pruner.getRemovedClasses()));
+ .prunedCopyFrom(
+ application,
+ CollectionUtils.mergeSets(prunedTypes, pruner.getRemovedClasses())));
// Print reasons on the application after pruning, so that we reflect the actual result.
if (whyAreYouKeepingConsumer != null) {
@@ -603,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..8b98f07 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
@@ -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/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/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/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/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index e55fc6e..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
@@ -161,6 +161,9 @@
public DexType parsedTypeName(String name) {
DexType type = appInfo.dexItemFactory.createType(getDescriptorFromClassBinaryName(name));
type = appView.graphLense().lookupType(type);
+ if (appInfo.wasPruned(type)) {
+ type = appInfo.dexItemFactory.objectType;
+ }
DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor);
renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
return type;
@@ -185,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 07023b5..3fbb72c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -60,6 +60,7 @@
import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
import com.android.tools.r8.shaking.RootSetBuilder.IfRuleEvaluator;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
@@ -392,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);
}
@@ -2255,7 +2256,7 @@
this.prunedTypes =
removedClasses == null
? previous.prunedTypes
- : mergeSets(previous.prunedTypes, removedClasses);
+ : CollectionUtils.mergeSets(previous.prunedTypes, removedClasses);
this.switchMaps = previous.switchMaps;
this.ordinalsMaps = previous.ordinalsMaps;
assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
@@ -2310,7 +2311,8 @@
// after second tree shaking.
this.callSites = previous.callSites;
this.brokenSuperInvokes = lense.rewriteMethodsConservatively(previous.brokenSuperInvokes);
- this.prunedTypes = rewriteItems(previous.prunedTypes, lense::lookupType);
+ // Don't rewrite pruned types - the removed types are identified by their original name.
+ this.prunedTypes = previous.prunedTypes;
this.mayHaveSideEffects =
rewriteReferenceKeys(previous.mayHaveSideEffects, lense::lookupReference);
this.noSideEffects = rewriteReferenceKeys(previous.noSideEffects, lense::lookupReference);
@@ -2561,13 +2563,6 @@
return instantiatedLambdas.contains(type);
}
- private static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) {
- ImmutableSet.Builder<T> builder = ImmutableSet.builder();
- builder.addAll(first);
- builder.addAll(second);
- return builder.build();
- }
-
@Override
public boolean hasLiveness() {
return true;
@@ -2608,6 +2603,10 @@
return prunedTypes.contains(type);
}
+ public Set<DexType> getPrunedTypes() {
+ return prunedTypes;
+ }
+
public DexEncodedMethod lookup(Type type, DexMethod target, DexType invocationContext) {
DexType holder = target.getHolder();
if (!holder.isClassType()) {
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/main/java/com/android/tools/r8/utils/CollectionUtils.java b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
new file mode 100644
index 0000000..6584f77
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
@@ -0,0 +1,18 @@
+// 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.utils;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Set;
+
+public class CollectionUtils {
+ public static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) {
+ ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+ builder.addAll(first);
+ builder.addAll(second);
+ return builder.build();
+ }
+}
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/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/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/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/b126592786/B126592786.java b/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
new file mode 100644
index 0000000..0971e8c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
@@ -0,0 +1,111 @@
+// 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.b126592786;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class B126592786 extends TestBase {
+
+ private final Backend backend;
+ private final boolean minify;
+
+ @Parameterized.Parameters(name = "Backend: {0} minify: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(Backend.values(), BooleanUtils.values());
+ }
+
+ public B126592786(Backend backend, boolean minify) {
+ this.backend = backend;
+ this.minify = minify;
+ }
+
+ public void runTest(boolean genericTypeLive) throws Exception {
+ Class<?> mainClass = genericTypeLive ? MainGenericTypeLive.class : MainGenericTypeNotLive.class;
+ testForR8(backend)
+ .minification(minify)
+ .addProgramClasses(A.class, GenericType.class, mainClass)
+ .addKeepMainRule(mainClass)
+ .addKeepRules(
+ "-keepclassmembers @" + Marker.class.getTypeName() + " class * {",
+ " <fields>;",
+ "}",
+ "-keepattributes InnerClasses,EnclosingMethod,Signature ")
+ .compile()
+ .inspect(inspector -> {
+ String genericTypeDescriptor = "Ljava/lang/Object;";
+ if (genericTypeLive) {
+ ClassSubject genericType = inspector.clazz(GenericType.class);
+ assertThat(genericType, isRenamed(minify));
+ genericTypeDescriptor = genericType.getFinalDescriptor();
+ }
+ String expectedSignature = "Ljava/util/List<" + genericTypeDescriptor + ">;";
+ FieldSubject list = inspector.clazz(A.class).uniqueFieldWithName("list");
+ assertThat(list, isPresent());
+ assertThat(list.getSignatureAnnotation(), isPresent());
+ assertEquals(expectedSignature, list.getSignatureAnnotationValue());
+ })
+ .run(mainClass)
+ .assertSuccess();
+ }
+
+ @Test
+ public void testGenericClassNotLive() throws Exception {
+ runTest(false);
+ }
+
+ @Test
+ public void testGenericClassLive() throws Exception {
+ runTest(true);
+ }
+}
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@interface Marker {
+}
+
+@Marker
+class A {
+
+ List<GenericType> list;
+}
+
+@Marker
+class GenericType {
+
+}
+
+class MainGenericTypeNotLive {
+
+ public static void main(String[] args) {
+ System.out.println(A.class);
+ }
+}
+
+class MainGenericTypeLive {
+
+ public static void main(String[] args) {
+ System.out.println(A.class);
+ System.out.println(GenericType.class);
+ }
+}
\ No newline at end of file
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/AbsentFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
index 3f8cd81..20da5d0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
@@ -90,4 +90,9 @@
public String getFinalSignatureAttribute() {
return null;
}
+
+ @Override
+ public AnnotationSubject annotation(String name) {
+ return new AbsentAnnotationSubject();
+ }
}
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/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index df23da9..0d63e38 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils.codeinspector;
+import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.naming.MemberNaming;
@@ -128,6 +129,14 @@
}
@Override
+ public AnnotationSubject annotation(String name) {
+ DexAnnotation annotation = codeInspector.findAnnotation(name, dexField.annotations);
+ return annotation == null
+ ? new AbsentAnnotationSubject()
+ : new FoundAnnotationSubject(annotation);
+ }
+
+ @Override
public String toString() {
return dexField.toSourceString();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index dd7cd85..4d7c0fc 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -288,5 +288,4 @@
? new AbsentAnnotationSubject()
: new FoundAnnotationSubject(annotation);
}
-
}
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/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
index af1fe14..a5cca23 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.utils.codeinspector;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.naming.MemberNaming.Signature;
public abstract class MemberSubject extends Subject {
@@ -48,6 +51,34 @@
return finalSignature == null ? null : finalSignature.name;
}
+ public abstract AnnotationSubject annotation(String name);
+
+ public AnnotationSubject getSignatureAnnotation() {
+ return annotation("dalvik.annotation.Signature");
+ }
+
+ public String getSignatureAnnotationValue() {
+ AnnotationSubject annotation = getSignatureAnnotation();
+ if (!annotation.isPresent()) {
+ return null;
+ }
+
+ assert annotation.getAnnotation().elements.length == 1;
+ DexAnnotationElement element = annotation.getAnnotation().elements[0];
+ assert element.name.toString().equals("value");
+ assert element.value instanceof DexValue.DexValueArray;
+ DexValue.DexValueArray array = (DexValue.DexValueArray) element.value;
+ StringBuilder builder = new StringBuilder();
+ for (DexValue value : array.getValues()) {
+ if (value instanceof DexValueString) {
+ builder.append(((DexValueString) value).value);
+ } else {
+ builder.append(value.toString());
+ }
+ }
+ return builder.toString();
+ }
+
public FieldSubject asFieldSubject() {
return null;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 1575372..8fb4001 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -72,6 +72,4 @@
public boolean isMethodSubject() {
return true;
}
-
- public abstract AnnotationSubject annotation(String name);
}
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 c842c69..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,6 +91,11 @@
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()
if not utils.is_bot() and not options.dry_run:
@@ -99,6 +105,7 @@
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/run_on_as_app.py b/tools/run_on_as_app.py
index d95453f..aa7ed29 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -511,8 +511,9 @@
apk_dest = os.path.join(out_dir, unsigned_apk_name)
as_utils.MoveFile(unsigned_apk, apk_dest, quiet=options.quiet)
- assert ('r8' not in shrinker
- or CheckIsBuiltWithExpectedR8(apk_dest, temp_dir, shrinker, options))
+ # TODO(mkroghj) Re-enable this assertion when fix works in Golem.
+ # assert ('r8' not in shrinker
+ # or CheckIsBuiltWithExpectedR8(apk_dest, temp_dir, shrinker, options))
profile_dest_dir = os.path.join(out_dir, 'profile')
as_utils.MoveProfileReportTo(profile_dest_dir, stdout, quiet=options.quiet)