Merge commit '17495d90b34de7210672c7715b0aa5d734dbc094' into dev-release
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 049699a..d46b12d 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@
"configuration_format_version": 3,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.0.10",
+ "version": "1.0.11",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
"common_flags": [
diff --git a/src/library_desugar/java/java/util/DoubleSummaryStatisticsConversions.java b/src/library_desugar/java/java/util/DoubleSummaryStatisticsConversions.java
index a0f6c8d..63845fc 100644
--- a/src/library_desugar/java/java/util/DoubleSummaryStatisticsConversions.java
+++ b/src/library_desugar/java/java/util/DoubleSummaryStatisticsConversions.java
@@ -4,80 +4,19 @@
package java.util;
-import java.lang.reflect.Field;
-
public class DoubleSummaryStatisticsConversions {
- private static final Field JAVA_LONG_COUNT_FIELD;
- private static final Field JAVA_DOUBLE_SUM_FIELD;
- private static final Field JAVA_DOUBLE_MIN_FIELD;
- private static final Field JAVA_DOUBLE_MAX_FIELD;
- private static final Field JD_LONG_COUNT_FIELD;
- private static final Field JD_DOUBLE_SUM_FIELD;
- private static final Field JD_DOUBLE_MIN_FIELD;
- private static final Field JD_DOUBLE_MAX_FIELD;
-
- static {
- Class<?> javaDoubleSummaryStatisticsClass = java.util.DoubleSummaryStatistics.class;
- JAVA_LONG_COUNT_FIELD = getField(javaDoubleSummaryStatisticsClass, "count");
- JAVA_LONG_COUNT_FIELD.setAccessible(true);
- JAVA_DOUBLE_SUM_FIELD = getField(javaDoubleSummaryStatisticsClass, "sum");
- JAVA_DOUBLE_SUM_FIELD.setAccessible(true);
- JAVA_DOUBLE_MIN_FIELD = getField(javaDoubleSummaryStatisticsClass, "min");
- JAVA_DOUBLE_MIN_FIELD.setAccessible(true);
- JAVA_DOUBLE_MAX_FIELD = getField(javaDoubleSummaryStatisticsClass, "max");
- JAVA_DOUBLE_MAX_FIELD.setAccessible(true);
-
- Class<?> jdDoubleSummaryStatisticsClass = j$.util.DoubleSummaryStatistics.class;
- JD_LONG_COUNT_FIELD = getField(jdDoubleSummaryStatisticsClass, "count");
- JD_LONG_COUNT_FIELD.setAccessible(true);
- JD_DOUBLE_SUM_FIELD = getField(jdDoubleSummaryStatisticsClass, "sum");
- JD_DOUBLE_SUM_FIELD.setAccessible(true);
- JD_DOUBLE_MIN_FIELD = getField(jdDoubleSummaryStatisticsClass, "min");
- JD_DOUBLE_MIN_FIELD.setAccessible(true);
- JD_DOUBLE_MAX_FIELD = getField(jdDoubleSummaryStatisticsClass, "max");
- JD_DOUBLE_MAX_FIELD.setAccessible(true);
- }
-
private DoubleSummaryStatisticsConversions() {}
- private static Field getField(Class<?> clazz, String name) {
- try {
- return clazz.getDeclaredField(name);
- } catch (NoSuchFieldException e) {
- throw new Error("Failed summary statistics set-up.", e);
- }
- }
-
public static j$.util.DoubleSummaryStatistics convert(java.util.DoubleSummaryStatistics stats) {
- if (stats == null) {
- return null;
- }
- j$.util.DoubleSummaryStatistics newInstance = new j$.util.DoubleSummaryStatistics();
- try {
- JD_LONG_COUNT_FIELD.set(newInstance, stats.getCount());
- JD_DOUBLE_SUM_FIELD.set(newInstance, stats.getSum());
- JD_DOUBLE_MIN_FIELD.set(newInstance, stats.getMin());
- JD_DOUBLE_MAX_FIELD.set(newInstance, stats.getMax());
- } catch (IllegalAccessException e) {
- throw new Error("Failed summary statistics conversion.", e);
- }
- return newInstance;
+ throw new Error(
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " to java.util.DoubleSummaryStatistics");
}
public static java.util.DoubleSummaryStatistics convert(j$.util.DoubleSummaryStatistics stats) {
- if (stats == null) {
- return null;
- }
- java.util.DoubleSummaryStatistics newInstance = new java.util.DoubleSummaryStatistics();
- try {
- JAVA_LONG_COUNT_FIELD.set(newInstance, stats.getCount());
- JAVA_DOUBLE_SUM_FIELD.set(newInstance, stats.getSum());
- JAVA_DOUBLE_MIN_FIELD.set(newInstance, stats.getMin());
- JAVA_DOUBLE_MAX_FIELD.set(newInstance, stats.getMax());
- } catch (IllegalAccessException e) {
- throw new Error("Failed summary statistics conversion.", e);
- }
- return newInstance;
+ throw new Error(
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " from java.util.DoubleSummaryStatistics");
}
}
diff --git a/src/library_desugar/java/java/util/IntSummaryStatisticsConversions.java b/src/library_desugar/java/java/util/IntSummaryStatisticsConversions.java
index 8e9b616..3645a41 100644
--- a/src/library_desugar/java/java/util/IntSummaryStatisticsConversions.java
+++ b/src/library_desugar/java/java/util/IntSummaryStatisticsConversions.java
@@ -4,81 +4,19 @@
package java.util;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-
public class IntSummaryStatisticsConversions {
- private static final Field JAVA_LONG_COUNT_FIELD;
- private static final Field JAVA_LONG_SUM_FIELD;
- private static final Field JAVA_INT_MIN_FIELD;
- private static final Field JAVA_INT_MAX_FIELD;
- private static final Field JD_LONG_COUNT_FIELD;
- private static final Field JD_LONG_SUM_FIELD;
- private static final Field JD_INT_MIN_FIELD;
- private static final Field JD_INT_MAX_FIELD;
-
- static {
- Class<?> javaIntSummaryStatisticsClass = java.util.IntSummaryStatistics.class;
- JAVA_LONG_COUNT_FIELD = getField(javaIntSummaryStatisticsClass, "count");
- JAVA_LONG_COUNT_FIELD.setAccessible(true);
- JAVA_LONG_SUM_FIELD = getField(javaIntSummaryStatisticsClass, "sum");
- JAVA_LONG_SUM_FIELD.setAccessible(true);
- JAVA_INT_MIN_FIELD = getField(javaIntSummaryStatisticsClass, "min");
- JAVA_INT_MIN_FIELD.setAccessible(true);
- JAVA_INT_MAX_FIELD = getField(javaIntSummaryStatisticsClass, "max");
- JAVA_INT_MAX_FIELD.setAccessible(true);
-
- Class<?> jdIntSummaryStatisticsClass = j$.util.IntSummaryStatistics.class;
- JD_LONG_COUNT_FIELD = getField(jdIntSummaryStatisticsClass, "count");
- JD_LONG_COUNT_FIELD.setAccessible(true);
- JD_LONG_SUM_FIELD = getField(jdIntSummaryStatisticsClass, "sum");
- JD_LONG_SUM_FIELD.setAccessible(true);
- JD_INT_MIN_FIELD = getField(jdIntSummaryStatisticsClass, "min");
- JD_INT_MIN_FIELD.setAccessible(true);
- JD_INT_MAX_FIELD = getField(jdIntSummaryStatisticsClass, "max");
- JD_INT_MAX_FIELD.setAccessible(true);
- }
-
private IntSummaryStatisticsConversions() {}
- private static Field getField(Class<?> clazz, String name) {
- try {
- return clazz.getDeclaredField(name);
- } catch (NoSuchFieldException e) {
- throw new Error("Failed summary statistics set-up.", e);
- }
- }
-
public static j$.util.IntSummaryStatistics convert(java.util.IntSummaryStatistics stats) {
- if (stats == null) {
- return null;
- }
- j$.util.IntSummaryStatistics newInstance = new j$.util.IntSummaryStatistics();
- try {
- JD_LONG_COUNT_FIELD.set(newInstance, stats.getCount());
- JD_LONG_SUM_FIELD.set(newInstance, stats.getSum());
- JD_INT_MIN_FIELD.set(newInstance, stats.getMin());
- JD_INT_MAX_FIELD.set(newInstance, stats.getMax());
- } catch (IllegalAccessException e) {
- throw new Error("Failed summary statistics conversion.", e);
- }
- return newInstance;
+ throw new Error(
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " to java.util.IntSummaryStatistics");
}
public static java.util.IntSummaryStatistics convert(j$.util.IntSummaryStatistics stats) {
- if (stats == null) {
- return null;
- }
- java.util.IntSummaryStatistics newInstance = new java.util.IntSummaryStatistics();
- try {
- JAVA_LONG_COUNT_FIELD.set(newInstance, stats.getCount());
- JAVA_LONG_SUM_FIELD.set(newInstance, stats.getSum());
- JAVA_INT_MIN_FIELD.set(newInstance, stats.getMin());
- JAVA_INT_MAX_FIELD.set(newInstance, stats.getMax());
- } catch (IllegalAccessException e) {
- throw new Error("Failed summary statistics conversion.", e);
- }
- return newInstance;
+ throw new Error(
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " from java.util.IntSummaryStatistics");
}
}
diff --git a/src/library_desugar/java/java/util/LongSummaryStatisticsConversions.java b/src/library_desugar/java/java/util/LongSummaryStatisticsConversions.java
index ae21eb2..af2c95b 100644
--- a/src/library_desugar/java/java/util/LongSummaryStatisticsConversions.java
+++ b/src/library_desugar/java/java/util/LongSummaryStatisticsConversions.java
@@ -4,81 +4,19 @@
package java.util;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-
public class LongSummaryStatisticsConversions {
- private static final Field JAVA_LONG_COUNT_FIELD;
- private static final Field JAVA_LONG_SUM_FIELD;
- private static final Field JAVA_LONG_MIN_FIELD;
- private static final Field JAVA_LONG_MAX_FIELD;
- private static final Field JD_LONG_COUNT_FIELD;
- private static final Field JD_LONG_SUM_FIELD;
- private static final Field JD_LONG_MIN_FIELD;
- private static final Field JD_LONG_MAX_FIELD;
-
- static {
- Class<?> javaLongSummaryStatisticsClass = java.util.LongSummaryStatistics.class;
- JAVA_LONG_COUNT_FIELD = getField(javaLongSummaryStatisticsClass, "count");
- JAVA_LONG_COUNT_FIELD.setAccessible(true);
- JAVA_LONG_SUM_FIELD = getField(javaLongSummaryStatisticsClass, "sum");
- JAVA_LONG_SUM_FIELD.setAccessible(true);
- JAVA_LONG_MIN_FIELD = getField(javaLongSummaryStatisticsClass, "min");
- JAVA_LONG_MIN_FIELD.setAccessible(true);
- JAVA_LONG_MAX_FIELD = getField(javaLongSummaryStatisticsClass, "max");
- JAVA_LONG_MAX_FIELD.setAccessible(true);
-
- Class<?> jdLongSummaryStatisticsClass = j$.util.LongSummaryStatistics.class;
- JD_LONG_COUNT_FIELD = getField(jdLongSummaryStatisticsClass, "count");
- JD_LONG_COUNT_FIELD.setAccessible(true);
- JD_LONG_SUM_FIELD = getField(jdLongSummaryStatisticsClass, "sum");
- JD_LONG_SUM_FIELD.setAccessible(true);
- JD_LONG_MIN_FIELD = getField(jdLongSummaryStatisticsClass, "min");
- JD_LONG_MIN_FIELD.setAccessible(true);
- JD_LONG_MAX_FIELD = getField(jdLongSummaryStatisticsClass, "max");
- JD_LONG_MAX_FIELD.setAccessible(true);
- }
-
private LongSummaryStatisticsConversions() {}
- private static Field getField(Class<?> clazz, String name) {
- try {
- return clazz.getDeclaredField(name);
- } catch (NoSuchFieldException e) {
- throw new Error("Failed summary statistics set-up.", e);
- }
- }
-
public static j$.util.LongSummaryStatistics convert(java.util.LongSummaryStatistics stats) {
- if (stats == null) {
- return null;
- }
- j$.util.LongSummaryStatistics newInstance = new j$.util.LongSummaryStatistics();
- try {
- JD_LONG_COUNT_FIELD.set(newInstance, stats.getCount());
- JD_LONG_SUM_FIELD.set(newInstance, stats.getSum());
- JD_LONG_MIN_FIELD.set(newInstance, stats.getMin());
- JD_LONG_MAX_FIELD.set(newInstance, stats.getMax());
- } catch (IllegalAccessException e) {
- throw new Error("Failed summary statistics conversion.", e);
- }
- return newInstance;
+ throw new Error(
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " to java.util.LongSummaryStatistics");
}
public static java.util.LongSummaryStatistics convert(j$.util.LongSummaryStatistics stats) {
- if (stats == null) {
- return null;
- }
- java.util.LongSummaryStatistics newInstance = new java.util.LongSummaryStatistics();
- try {
- JAVA_LONG_COUNT_FIELD.set(newInstance, stats.getCount());
- JAVA_LONG_SUM_FIELD.set(newInstance, stats.getSum());
- JAVA_LONG_MIN_FIELD.set(newInstance, stats.getMin());
- JAVA_LONG_MAX_FIELD.set(newInstance, stats.getMax());
- } catch (IllegalAccessException e) {
- throw new Error("Failed summary statistics conversion.", e);
- }
- return newInstance;
+ throw new Error(
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " from java.util.LongSummaryStatistics");
}
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 07948a1..17ee9af 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -6,6 +6,8 @@
import static com.android.tools.r8.R8Command.USAGE_MESSAGE;
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
@@ -16,10 +18,15 @@
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.AppliedGraphLens;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
@@ -609,11 +616,6 @@
}
}
- // Overwrite SourceFile if specified. This step should be done after IR conversion.
- timing.begin("Rename SourceFile");
- new SourceFileRewriter(appViewWithLiveness).run();
- timing.end();
-
// Collect the already pruned types before creating a new app info without liveness.
Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
@@ -699,7 +701,8 @@
executorService,
timing)
.withEnumValueInfoMaps(enumValueInfoMapCollection));
-
+ // Rerunning the enqueuer should not give rise to any method rewritings.
+ assert enqueuer.buildGraphLense(appView) == appView.graphLense();
appView.withGeneratedMessageLiteBuilderShrinker(
shrinker ->
shrinker.rewriteDeadBuilderReferencesFromDynamicMethods(
@@ -815,6 +818,8 @@
new KotlinMetadataRewriter(appView, namingLens).run(executorService);
timing.end();
+ assert verifyMovedMethodsHaveOriginalMethodPosition(appView, application);
+
timing.begin("Line number remapping");
// When line number optimization is turned off the identity mapping for line numbers is
// used. We still run the line number optimizer to collect line numbers and inline frame
@@ -823,6 +828,11 @@
LineNumberOptimizer.run(appView, application, inputApp, namingLens);
timing.end();
+ // Overwrite SourceFile if specified. This step should be done after IR conversion.
+ timing.begin("Rename SourceFile");
+ new SourceFileRewriter(appView, application).run();
+ timing.end();
+
// If a method filter is present don't produce output since the application is likely partial.
if (options.hasMethodsFilter()) {
System.out.println("Finished compilation with method filter: ");
@@ -893,6 +903,58 @@
}
}
+ private static boolean verifyMovedMethodsHaveOriginalMethodPosition(
+ AppView<?> appView, DirectMappedDexApplication application) {
+ application
+ .classes()
+ .forEach(
+ clazz -> {
+ clazz.forEachProgramMethod(
+ method -> {
+ DexMethod originalMethod =
+ appView.graphLense().getOriginalMethodSignature(method.getReference());
+ if (originalMethod != method.getReference()) {
+ DexMethod originalMethod2 =
+ appView.graphLense().getOriginalMethodSignature(method.getReference());
+ appView.graphLense().getOriginalMethodSignature(method.getReference());
+ DexEncodedMethod definition = method.getDefinition();
+ Code code = definition.getCode();
+ if (code == null) {
+ return;
+ }
+ if (code.isCfCode()) {
+ assert verifyOriginalMethodInPosition(code.asCfCode(), originalMethod);
+ } else {
+ assert code.isDexCode();
+ assert verifyOriginalMethodInDebugInfo(code.asDexCode(), originalMethod);
+ }
+ }
+ });
+ });
+ return true;
+ }
+
+ private static boolean verifyOriginalMethodInPosition(CfCode code, DexMethod originalMethod) {
+ for (CfInstruction instruction : code.instructions) {
+ if (!instruction.isPosition()) {
+ continue;
+ }
+ CfPosition position = instruction.asPosition();
+ assert position.getPosition().getOutermostCaller().method == originalMethod;
+ }
+ return true;
+ }
+
+ private static boolean verifyOriginalMethodInDebugInfo(DexCode code, DexMethod originalMethod) {
+ if (code.getDebugInfo() == null) {
+ return true;
+ }
+ for (DexDebugEvent event : code.getDebugInfo().events) {
+ assert !event.isSetInlineFrame() || event.asSetInlineFrame().hasOuterPosition(originalMethod);
+ }
+ return true;
+ }
+
private AppView<AppInfoWithLiveness> runEnqueuer(
AnnotationRemover.Builder annotationRemoverBuilder,
ExecutorService executorService,
@@ -917,6 +979,7 @@
options.getProguardConfiguration().getDontWarnPatterns(),
executorService,
timing));
+ appView.setGraphLense(enqueuer.buildGraphLense(appView));
if (InternalOptions.assertionsEnabled()) {
// Register the dead proto types. These are needed to verify that no new missing types are
// reported and that no dead proto types are referenced in the generated application.
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index f790131..059f7f6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -38,6 +38,14 @@
public abstract void accept(DexDebugEventVisitor visitor);
+ public boolean isSetInlineFrame() {
+ return false;
+ }
+
+ public SetInlineFrame asSetInlineFrame() {
+ return null;
+ }
+
public static class AdvancePC extends DexDebugEvent {
public final int delta;
@@ -421,6 +429,21 @@
SetInlineFrame o = (SetInlineFrame) other;
return callee == o.callee && Objects.equals(caller, o.caller);
}
+
+ @Override
+ public boolean isSetInlineFrame() {
+ return true;
+ }
+
+ @Override
+ public SetInlineFrame asSetInlineFrame() {
+ return this;
+ }
+
+ public boolean hasOuterPosition(DexMethod method) {
+ return (caller == null && callee == method)
+ || (caller != null && caller.getOutermostCaller().method == method);
+ }
}
public static class Default extends DexDebugEvent {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 5cbe06a..2269517 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -36,6 +36,7 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.ValueType;
@@ -858,6 +859,36 @@
}
}
+ public static void setOriginalMethodPosition(Code code, DexMethod originalMethod) {
+ if (code.isDexCode()) {
+ DexCode dexCode = code.asDexCode();
+ DexDebugInfo debugInfo = dexCode.getDebugInfo();
+ if (debugInfo == null) {
+ return;
+ }
+ for (DexDebugEvent event : debugInfo.events) {
+ if (event.isSetInlineFrame() && event.asSetInlineFrame().hasOuterPosition(originalMethod)) {
+ return;
+ }
+ }
+ DexDebugEvent[] newEvents = new DexDebugEvent[debugInfo.events.length + 1];
+ newEvents[0] = new SetInlineFrame(originalMethod, null);
+ System.arraycopy(debugInfo.events, 0, newEvents, 1, debugInfo.events.length);
+ dexCode.setDebugInfo(new DexDebugInfo(debugInfo.startLine, debugInfo.parameters, newEvents));
+ } else {
+ assert code.isCfCode();
+ CfCode cfCode = code.asCfCode();
+ for (CfInstruction instruction : cfCode.instructions) {
+ if (instruction.isPosition()) {
+ assert instruction.asPosition().getPosition().getOutermostCaller().method
+ == originalMethod;
+ return;
+ }
+ }
+ assert false : "The original position should be present in the CF code.";
+ }
+ }
+
private DexEncodedMethod toMethodThatLogsErrorDexCode(DexItemFactory itemFactory) {
checkIfObsolete();
Signature signature = MethodSignature.fromDexMethod(method);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index 6ed94b4..d11bf38 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -85,9 +85,8 @@
assert position.isNone();
position = Position.noneWithMethod(context.getReference(), null);
}
- assert position.callerPosition == null
- || position.getOutermostCaller().method
- == appView.graphLense().getOriginalMethodSignature(context.getReference());
+ assert position.getOutermostCaller().method
+ == appView.graphLense().getOriginalMethodSignature(context.getReference());
return position;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 00eb737..9055230 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import static com.android.tools.r8.ir.conversion.CfSourceUtils.ensureLabel;
+
import com.android.tools.r8.cf.CfRegisterAllocator;
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
@@ -322,7 +324,7 @@
} while (block != null);
// TODO(mkroghj) Move computation of stack-height to CF instructions.
if (!openLocalVariables.isEmpty()) {
- CfLabel endLabel = ensureLabel();
+ CfLabel endLabel = ensureLabel(instructions);
for (LocalVariableInfo info : openLocalVariables.values()) {
info.setEnd(endLabel);
localVariablesTable.add(info);
@@ -460,7 +462,7 @@
}
} else {
if (instruction.isNewInstance()) {
- newInstanceLabels.put(instruction.asNewInstance(), ensureLabel());
+ newInstanceLabels.put(instruction.asNewInstance(), ensureLabel(instructions));
}
updatePositionAndLocals(instruction);
instruction.buildCf(this);
@@ -481,7 +483,7 @@
if (!didLocalsChange && !didPositionChange) {
return;
}
- CfLabel label = ensureLabel();
+ CfLabel label = ensureLabel(instructions);
if (didLocalsChange) {
updateLocals(label);
}
@@ -526,20 +528,6 @@
return pendingLocalChanges;
}
- private CfLabel ensureLabel() {
- CfInstruction last = getLastInstruction();
- if (last instanceof CfLabel) {
- return (CfLabel) last;
- }
- CfLabel label = new CfLabel();
- add(label);
- return label;
- }
-
- private CfInstruction getLastInstruction() {
- return instructions.isEmpty() ? null : instructions.get(instructions.size() - 1);
- }
-
private void addFrame(BasicBlock block) {
List<TypeInfo> stack = registerAllocator.getTypesAtBlockEntry(block).stack;
List<FrameType> stackTypes;
@@ -570,7 +558,7 @@
// CfCode.
boolean didLocalsChange = localsChanged();
if (didLocalsChange) {
- CfLabel label = ensureLabel();
+ CfLabel label = ensureLabel(instructions);
updateLocals(label);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceUtils.java
new file mode 100644
index 0000000..9c3a2f1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceUtils.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfLabel;
+import java.util.List;
+
+public class CfSourceUtils {
+
+ public static CfLabel ensureLabel(List<CfInstruction> instructions) {
+ CfInstruction last = getLastInstruction(instructions);
+ if (last != null && last.isLabel()) {
+ return last.asLabel();
+ }
+ CfLabel label = new CfLabel();
+ instructions.add(label);
+ return label;
+ }
+
+ private static CfInstruction getLastInstruction(List<CfInstruction> instructions) {
+ return instructions.isEmpty() ? null : instructions.get(instructions.size() - 1);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index a277a71..2b8ce66 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.desugar;
+import static com.android.tools.r8.graph.DexEncodedMethod.setOriginalMethodPosition;
+
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.Instruction;
@@ -94,6 +96,12 @@
newFlags.promoteToStatic();
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companionMethod.getArity(), appView);
+ if (!appView.options().isDesugaredLibraryCompilation()) {
+ setOriginalMethodPosition(
+ code, appView.graphLense().getOriginalMethodSignature(virtual.method));
+ } else {
+ assert appView.graphLense().isIdentityLense();
+ }
DexEncodedMethod implMethod =
new DexEncodedMethod(
companionMethod,
@@ -128,13 +136,18 @@
if (originalFlags.isPrivate()) {
newFlags.promoteToPublic();
}
-
DexMethod oldMethod = direct.method;
if (isStaticMethod(direct)) {
assert originalFlags.isPrivate() || originalFlags.isPublic()
: "Static interface method " + direct.toSourceString() + " is expected to "
+ "either be public or private in " + iface.origin;
DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
+ if (!appView.options().isDesugaredLibraryCompilation()) {
+ setOriginalMethodPosition(
+ direct.getCode(), appView.graphLense().getOriginalMethodSignature(oldMethod));
+ } else {
+ assert appView.graphLense().isIdentityLense();
+ }
DexEncodedMethod implMethod =
new DexEncodedMethod(
companionMethod,
@@ -162,6 +175,12 @@
}
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companionMethod.getArity(), appView);
+ if (!appView.options().isDesugaredLibraryCompilation()) {
+ setOriginalMethodPosition(
+ code, appView.graphLense().getOriginalMethodSignature(oldMethod));
+ } else {
+ assert appView.graphLense().isIdentityLense();
+ }
DexEncodedMethod implMethod =
new DexEncodedMethod(
companionMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index d203a12..e40e0e4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -670,8 +670,7 @@
encodedMethod -> {
assert encodedMethod.isDirectMethod();
// We need to create a new method with the same code to be able to safely relax
- // its
- // accessibility and make it virtual.
+ // its accessibility and make it virtual.
MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
newAccessFlags.unsetPrivate();
newAccessFlags.setPublic();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 8a7b983..fb66a2a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -7,10 +7,13 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -29,6 +32,7 @@
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -342,4 +346,35 @@
public Map<DexType, LambdaClass> getKnownLambdaClasses() {
return knownLambdaClasses;
}
+
+ public GraphLense buildMappingLense(AppView<?> appView) {
+ if (originalMethodSignatures.isEmpty()) {
+ return appView.graphLense();
+ }
+ return new LambdaRewriterLense(
+ originalMethodSignatures, appView.graphLense(), appView.dexItemFactory());
+ }
+
+ static class LambdaRewriterLense extends NestedGraphLense {
+
+ public LambdaRewriterLense(
+ BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ GraphLense graphLense,
+ DexItemFactory factory) {
+ super(
+ ImmutableMap.of(),
+ ImmutableMap.of(),
+ ImmutableMap.of(),
+ null,
+ originalMethodSignatures,
+ graphLense,
+ factory);
+ }
+
+ @Override
+ protected boolean isLegitimateToHaveEmptyMappings() {
+ return true;
+ }
+ }
}
+
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
index 3feac87..f80e1d7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
@@ -12,8 +12,11 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
@@ -23,6 +26,7 @@
import java.util.Map;
import java.util.Set;
+// TODO(b/160634549): Rename or refactor this to reflect its non-cost related analysis.
/** Analysis that estimates the cost of class inlining an object allocation. */
class ClassInlinerCostAnalysis {
@@ -73,6 +77,28 @@
continue;
}
IRCode inliningIR = inliningIRProvider.getAndCacheInliningIR(invoke, inlinee);
+
+ // If the instance is part of a phi, then inlining will invalidate the inliner assumptions.
+ // TODO(b/160634549): This is not a budget miss but a hard requirement.
+ InstructionIterator iterator = inliningIR.entryBlock().iterator();
+ while (iterator.hasNext()) {
+ Instruction next = iterator.next();
+ if (!next.isArgument()) {
+ break;
+ }
+ Value argumentValue = next.outValue();
+ TypeElement argumentType = argumentValue.getType();
+ if (argumentType.isClassType()
+ && argumentType.asClassType().getClassType() == eligibleClass.type) {
+ assert argumentValue.uniqueUsers().stream()
+ .noneMatch(
+ AssumeAndCheckCastAliasedValueConfiguration.getInstance()::isIntroducingAnAlias);
+ if (argumentValue.hasPhiUsers()) {
+ return true;
+ }
+ }
+ }
+
int increment =
inlinee.getDefinition().getCode().estimatedSizeForInlining()
- estimateNumberOfNonMaterializingInstructions(invoke, inliningIR);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index e03bc6b..1ffca3d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
@@ -162,7 +163,7 @@
analyzeCheckCast(instruction.asCheckCast(), eligibleEnums);
break;
case Opcodes.INVOKE_STATIC:
- analyzeInvokeStatic(instruction.asInvokeStatic(), eligibleEnums);
+ analyzeInvokeStatic(instruction.asInvokeStatic(), eligibleEnums, code.context());
break;
case Opcodes.STATIC_GET:
case Opcodes.INSTANCE_GET:
@@ -210,11 +211,17 @@
}
}
- private void analyzeInvokeStatic(InvokeStatic invokeStatic, Set<DexType> eligibleEnums) {
+ private void analyzeInvokeStatic(
+ InvokeStatic invokeStatic, Set<DexType> eligibleEnums, ProgramMethod context) {
DexMethod invokedMethod = invokeStatic.getInvokedMethod();
DexProgramClass enumClass = getEnumUnboxingCandidateOrNull(invokedMethod.holder);
if (enumClass != null) {
- eligibleEnums.add(enumClass.type);
+ DexEncodedMethod method = invokeStatic.lookupSingleTarget(appView, context);
+ if (method != null) {
+ eligibleEnums.add(enumClass.type);
+ } else {
+ markEnumAsUnboxable(Reason.INVALID_INVOKE, enumClass);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index 2cd8633..789f4d9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -75,6 +75,12 @@
boolean isOrdinalInvoke = invokedMethod == factory.enumMethods.ordinal;
boolean isNameInvoke = invokedMethod == factory.enumMethods.name;
boolean isToStringInvoke = invokedMethod == factory.enumMethods.toString;
+
+ // TODO(b/160667929): Re-enable name()/toString() optimizations.
+ if (!isOrdinalInvoke) {
+ continue;
+ }
+
if (!isOrdinalInvoke && !isNameInvoke && !isToStringInvoke) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index e98d9b9..43a0511 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.naming.ClassNameMapper.MissingFileAction.MISSING_FILE_IS_ERROR;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -16,6 +17,7 @@
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.BiMapContainer;
import com.android.tools.r8.utils.ChainableStringConsumer;
+import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
@@ -68,7 +70,7 @@
public static ClassNameMapper mapperFromInputStream(InputStream in) throws IOException {
return mapperFromBufferedReader(
- new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)));
+ new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)), null);
}
public static ClassNameMapper mapperFromFile(Path path) throws IOException {
@@ -87,12 +89,20 @@
}
public static ClassNameMapper mapperFromString(String contents) throws IOException {
- return mapperFromBufferedReader(CharSource.wrap(contents).openBufferedStream());
+ return mapperFromBufferedReader(CharSource.wrap(contents).openBufferedStream(), null);
}
- private static ClassNameMapper mapperFromBufferedReader(BufferedReader reader)
- throws IOException {
- try (ProguardMapReader proguardReader = new ProguardMapReader(reader)) {
+ public static ClassNameMapper mapperFromString(
+ String contents, DiagnosticsHandler diagnosticsHandler) throws IOException {
+ return mapperFromBufferedReader(
+ CharSource.wrap(contents).openBufferedStream(), diagnosticsHandler);
+ }
+
+ private static ClassNameMapper mapperFromBufferedReader(
+ BufferedReader reader, DiagnosticsHandler diagnosticsHandler) throws IOException {
+ try (ProguardMapReader proguardReader =
+ new ProguardMapReader(
+ reader, diagnosticsHandler != null ? diagnosticsHandler : new Reporter())) {
ClassNameMapper.Builder builder = ClassNameMapper.builder();
proguardReader.parse(builder);
return builder.build();
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNaming.java b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
index 1404387..5a282d0 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
@@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.utils.ThrowingConsumer;
/**
@@ -14,8 +16,16 @@
public interface ClassNaming {
abstract class Builder {
+
public abstract Builder addMemberEntry(MemberNaming entry);
+ public abstract Builder addMappingInformation(MappingInformation mappingInformation);
+
+ public abstract Builder addMappingInformation(
+ MappingInformation mappingInformation,
+ DiagnosticsHandler diagnosticsHandler,
+ int lineNumber);
+
public abstract ClassNaming build();
/** This is an optional method, may be implemented as no-op */
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
index 65bc602..acbb577 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -10,6 +11,7 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -35,6 +37,7 @@
public class ClassNamingForMapApplier implements ClassNaming {
public static class Builder extends ClassNaming.Builder {
+
private final String originalName;
private final String renamedName;
private final Position position;
@@ -75,6 +78,21 @@
}
@Override
+ public ClassNaming.Builder addMappingInformation(MappingInformation mappingInformation) {
+ // Intentionally kept empty until we support additional information with -applymapping.
+ return this;
+ }
+
+ @Override
+ public ClassNaming.Builder addMappingInformation(
+ MappingInformation mappingInformation,
+ DiagnosticsHandler diagnosticsHandler,
+ int lineNumber) {
+ // Intentionally kept empty until we support additional information with -applymapping.
+ return this;
+ }
+
+ @Override
public ClassNamingForMapApplier build() {
return new ClassNamingForMapApplier(
renamedName, originalName, position, qualifiedMethodMembers, methodMembers, fieldMembers);
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 5a00fcb..98293ab 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -3,10 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.naming.MemberNaming.NoSignature.NO_SIGNATURE;
+
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
+import com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics;
import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.ImmutableMap;
@@ -19,6 +24,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.Consumer;
/**
* Stores name information for a class.
@@ -28,12 +34,14 @@
public class ClassNamingForNameMapper implements ClassNaming {
public static class Builder extends ClassNaming.Builder {
+
private final String originalName;
private final String renamedName;
private final Map<MethodSignature, MemberNaming> methodMembers = Maps.newHashMap();
private final Map<FieldSignature, MemberNaming> fieldMembers = Maps.newHashMap();
private final Map<String, List<MappedRange>> mappedRangesByName = Maps.newHashMap();
private final Map<String, List<MemberNaming>> mappedNamingsByName = Maps.newHashMap();
+ private final Map<Signature, List<MappingInformation>> additionalMappings = Maps.newHashMap();
private Builder(String renamedName, String originalName) {
this.originalName = originalName;
@@ -43,9 +51,9 @@
@Override
public ClassNaming.Builder addMemberEntry(MemberNaming entry) {
if (entry.isMethodNaming()) {
- methodMembers.put((MethodSignature) entry.getRenamedSignature(), entry);
+ methodMembers.put(entry.getRenamedSignature().asMethodSignature(), entry);
} else {
- fieldMembers.put((FieldSignature) entry.getRenamedSignature(), entry);
+ fieldMembers.put(entry.getRenamedSignature().asFieldSignature(), entry);
}
mappedNamingsByName
.computeIfAbsent(entry.getRenamedName(), m -> new ArrayList<>())
@@ -54,6 +62,45 @@
}
@Override
+ public ClassNaming.Builder addMappingInformation(MappingInformation mappingInformation) {
+ return addMappingInformation(
+ mappingInformation,
+ other -> {
+ assert false;
+ });
+ }
+
+ @Override
+ public ClassNaming.Builder addMappingInformation(
+ MappingInformation mappingInformation,
+ DiagnosticsHandler diagnosticsHandler,
+ int lineNumber) {
+ return addMappingInformation(
+ mappingInformation,
+ other ->
+ diagnosticsHandler.warning(
+ MappingInformationDiagnostics.notAllowedCombination(
+ originalName, renamedName, mappingInformation, other, lineNumber)));
+ }
+
+ private ClassNaming.Builder addMappingInformation(
+ MappingInformation mappingInformation, Consumer<MappingInformation> notAllowedCombination) {
+ Signature signature =
+ mappingInformation.isSignatureMappingInformation()
+ ? mappingInformation.asSignatureMappingInformation().getSignature()
+ : NO_SIGNATURE;
+ List<MappingInformation> additionalMappingForSignature =
+ additionalMappings.computeIfAbsent(signature, ignored -> new ArrayList<>());
+ for (MappingInformation information : additionalMappingForSignature) {
+ if (!information.allowOther(mappingInformation)) {
+ notAllowedCombination.accept(information);
+ }
+ }
+ additionalMappingForSignature.add(mappingInformation);
+ return this;
+ }
+
+ @Override
public ClassNamingForNameMapper build() {
Map<String, MappedRangesOfName> map;
@@ -67,7 +114,13 @@
}
return new ClassNamingForNameMapper(
- renamedName, originalName, methodMembers, fieldMembers, map, mappedNamingsByName);
+ renamedName,
+ originalName,
+ methodMembers,
+ fieldMembers,
+ map,
+ mappedNamingsByName,
+ additionalMappings);
}
/** The parameters are forwarded to MappedRange constructor, see explanation there. */
@@ -198,19 +251,23 @@
public final Map<String, List<MemberNaming>> mappedNamingsByName;
+ private final Map<Signature, List<MappingInformation>> additionalMappings;
+
private ClassNamingForNameMapper(
String renamedName,
String originalName,
Map<MethodSignature, MemberNaming> methodMembers,
Map<FieldSignature, MemberNaming> fieldMembers,
Map<String, MappedRangesOfName> mappedRangesByRenamedName,
- Map<String, List<MemberNaming>> mappedNamingsByName) {
+ Map<String, List<MemberNaming>> mappedNamingsByName,
+ Map<Signature, List<MappingInformation>> additionalMappings) {
this.renamedName = renamedName;
this.originalName = originalName;
this.methodMembers = ImmutableMap.copyOf(methodMembers);
this.fieldMembers = ImmutableMap.copyOf(fieldMembers);
this.mappedRangesByRenamedName = mappedRangesByRenamedName;
this.mappedNamingsByName = mappedNamingsByName;
+ this.additionalMappings = additionalMappings;
}
public MappedRangesOfName getMappedRangesForRenamedName(String renamedName) {
@@ -298,7 +355,16 @@
void write(ChainableStringConsumer consumer) {
consumer.accept(originalName).accept(" -> ").accept(renamedName).accept(":\n");
- // First print field member namings.
+ // Print all additional mapping information.
+ additionalMappings.forEach(
+ (signature, mappingInformations) -> {
+ assert !mappingInformations.isEmpty();
+ for (MappingInformation mappingInformation : mappingInformations) {
+ consumer.accept("# " + mappingInformation.serialize()).accept("\n");
+ }
+ });
+
+ // Print field member namings.
forAllFieldNaming(m -> consumer.accept(" ").accept(m.toString()).accept("\n"));
// Sort MappedRanges by sequence number to restore construction order (original Proguard-map
@@ -313,6 +379,10 @@
}
}
+ public Map<Signature, List<MappingInformation>> getAdditionalMappings() {
+ return additionalMappings;
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 84f726d..51c3272 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -58,12 +58,12 @@
final Position position;
public MemberNaming(Signature signature, String renamedName) {
- this(signature, renamedName, Position.UNKNOWN);
+ this(signature, signature.asRenamed(renamedName), Position.UNKNOWN);
}
- public MemberNaming(Signature signature, String renamedName, Position position) {
+ public MemberNaming(Signature signature, Signature renamedSignature, Position position) {
this.signature = signature;
- this.renamedSignature = signature.asRenamed(renamedName);
+ this.renamedSignature = renamedSignature;
this.position = position;
}
@@ -168,6 +168,40 @@
}
}
+ public static class NoSignature extends Signature {
+
+ public static final NoSignature NO_SIGNATURE = new NoSignature();
+
+ public NoSignature() {
+ super("NO SIGNATURE");
+ }
+
+ @Override
+ Signature asRenamed(String renamedName) {
+ throw new Unreachable("Should not be called on NoSignature");
+ }
+
+ @Override
+ public SignatureKind kind() {
+ throw new Unreachable("Should not be called on NoSignature");
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o == this;
+ }
+
+ @Override
+ public int hashCode() {
+ return 7;
+ }
+
+ @Override
+ void write(Writer builder) throws IOException {
+ throw new Unreachable("Should not be called on NoSignature");
+ }
+ }
+
public static class FieldSignature extends Signature {
public final String type;
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index 747423f..23a9a06 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -3,17 +3,24 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
+import com.android.tools.r8.naming.mappinginformation.SignatureMappingInformation;
import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.utils.IdentifierUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.Maps;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -54,16 +61,19 @@
public class ProguardMapReader implements AutoCloseable {
private final BufferedReader reader;
+ private final JsonParser jsonParser = new JsonParser();
+ private final DiagnosticsHandler diagnosticsHandler;
@Override
public void close() throws IOException {
- if (reader != null) {
- reader.close();
- }
+ reader.close();
}
- ProguardMapReader(BufferedReader reader) {
+ ProguardMapReader(BufferedReader reader, DiagnosticsHandler diagnosticsHandler) {
this.reader = reader;
+ this.diagnosticsHandler = diagnosticsHandler;
+ assert reader != null;
+ assert diagnosticsHandler != null;
}
// Internal parser state
@@ -118,7 +128,7 @@
for (int i = 0; i < line.length(); ++i) {
char c = line.charAt(i);
if (c == '#') {
- return true;
+ return !hasFirstCharJsonBrace(line, i);
} else if (!StringUtils.isWhitespace(c)) {
return false;
}
@@ -126,6 +136,33 @@
return true;
}
+ private boolean isCommentLineWithJsonBrace() {
+ if (line == null) {
+ return false;
+ }
+ for (int i = 0; i < line.length(); ++i) {
+ char c = line.charAt(i);
+ if (c == '#') {
+ return hasFirstCharJsonBrace(line, i);
+ } else if (!Character.isWhitespace(c)) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasFirstCharJsonBrace(String line, int commentCharIndex) {
+ for (int i = commentCharIndex + 1; i < line.length(); i++) {
+ char c = line.charAt(i);
+ if (c == '{') {
+ return true;
+ } else if (!Character.isWhitespace(c)) {
+ return false;
+ }
+ }
+ return false;
+ }
+
private boolean skipLine() throws IOException {
lineOffset = 0;
do {
@@ -213,10 +250,26 @@
MemberNaming lastAddedNaming = null;
MemberNaming activeMemberNaming = null;
Range previousMappedRange = null;
+ Map<Signature, SignatureMappingInformation> mappingInformation = Maps.newHashMap();
do {
Object originalRange = null;
Range mappedRange = null;
-
+ // Try to parse any information added in comments above member namings
+ if (isCommentLineWithJsonBrace()) {
+ MappingInformation mappingInfo =
+ MappingInformation.fromJsonObject(parseJsonInComment(), diagnosticsHandler, lineNo);
+ if (mappingInfo != null) {
+ if (mappingInfo.isSignatureMappingInformation()) {
+ SignatureMappingInformation sigMapInfo = mappingInfo.asSignatureMappingInformation();
+ mappingInformation.put(sigMapInfo.getSignature(), sigMapInfo);
+ } else {
+ classNamingBuilder.addMappingInformation(mappingInfo, diagnosticsHandler, lineNo);
+ }
+ }
+ // Skip reading the rest of the line.
+ lineOffset = line.length();
+ continue;
+ }
// Parse the member line ' x:y:name:z:q -> renamedName'.
if (!StringUtils.isWhitespace(peekCodePoint())) {
break;
@@ -274,7 +327,16 @@
}
}
}
- activeMemberNaming = new MemberNaming(signature, renamedName, getPosition());
+ if (mappingInformation.containsKey(signature)) {
+ activeMemberNaming =
+ new MemberNaming(
+ signature,
+ mappingInformation.get(signature).apply(signature, renamedName, diagnosticsHandler),
+ getPosition());
+ } else {
+ activeMemberNaming =
+ new MemberNaming(signature, signature.asRenamed(renamedName), getPosition());
+ }
previousMappedRange = mappedRange;
} while (nextLine());
@@ -451,6 +513,20 @@
return result;
}
+ private JsonObject parseJsonInComment() {
+ assert isCommentLineWithJsonBrace();
+ try {
+ int firstIndex = 0;
+ while (line.charAt(firstIndex) != '{') {
+ firstIndex++;
+ }
+ return jsonParser.parse(line.substring(firstIndex)).getAsJsonObject();
+ } catch (com.google.gson.JsonSyntaxException ex) {
+ // An info message is reported in MappingInformation.
+ return null;
+ }
+ }
+
private class ParseException extends RuntimeException {
private final int lineNo;
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
index be69416..5c50c06 100644
--- a/src/main/java/com/android/tools/r8/naming/SeedMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -74,7 +74,7 @@
private static SeedMapper seedMapperFromInputStream(Reporter reporter, InputStream in)
throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
- try (ProguardMapReader proguardReader = new ProguardMapReader(reader)) {
+ try (ProguardMapReader proguardReader = new ProguardMapReader(reader, reporter)) {
SeedMapper.Builder builder = SeedMapper.builder(reporter);
proguardReader.parse(builder);
return builder.build();
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
index 1e57331..e2bf357 100644
--- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -5,12 +5,12 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexDebugEvent.SetFile;
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfiguration;
import java.util.Arrays;
@@ -21,10 +21,12 @@
*/
public class SourceFileRewriter {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<?> appView;
+ private final DexApplication application;
- public SourceFileRewriter(AppView<AppInfoWithLiveness> appView) {
+ public SourceFileRewriter(AppView<?> appView, DexApplication application) {
this.appView = appView;
+ this.application = application;
}
public void run() {
@@ -37,16 +39,18 @@
&& appView.options().forceProguardCompatibility) {
return;
}
+ boolean isMinifying = appView.options().isMinifying();
+ assert !isMinifying || appView.appInfo().hasLiveness();
// Now, the user wants either to remove source file attribute or to rename it for non-kept
// classes.
DexString defaultRenaming = getSourceFileRenaming(proguardConfiguration);
- for (DexClass clazz : appView.appInfo().classes()) {
+ for (DexClass clazz : application.classes()) {
// We only parse sourceFile if -keepattributes SourceFile, but for compat we should add
// a source file name, otherwise line positions will not be printed on the JVM or old version
// of ART.
if (!hasRenameSourceFileAttribute
&& proguardConfiguration.getKeepAttributes().sourceFile
- && !appView.appInfo().isMinificationAllowed(clazz.type)) {
+ && !(isMinifying && appView.withLiveness().appInfo().isMinificationAllowed(clazz.type))) {
continue;
}
clazz.sourceFile = defaultRenaming;
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
new file mode 100644
index 0000000..87b3e7b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.mappinginformation;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+public class FileNameInformation extends MappingInformation {
+
+ private final String fileName;
+
+ public static final String ID = "sourceFile";
+ static final String FILE_NAME_KEY = "fileName";
+
+ private FileNameInformation(String fileName) {
+ super(NO_LINE_NUMBER);
+ this.fileName = fileName;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ @Override
+ public String serialize() {
+ JsonObject result = new JsonObject();
+ result.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
+ result.add(FILE_NAME_KEY, new JsonPrimitive(fileName));
+ return result.toString();
+ }
+
+ @Override
+ public boolean isFileNameInformation() {
+ return true;
+ }
+
+ @Override
+ public FileNameInformation asFileNameInformation() {
+ return this;
+ }
+
+ @Override
+ public boolean allowOther(MappingInformation information) {
+ return !information.isFileNameInformation();
+ }
+
+ public static FileNameInformation build(String fileName) {
+ return new FileNameInformation(fileName);
+ }
+
+ public static FileNameInformation build(
+ JsonObject object, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ try {
+ JsonElement fileName =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, FILE_NAME_KEY, ID);
+ if (fileName == null) {
+ return null;
+ }
+ return new FileNameInformation(fileName.getAsString());
+ } catch (UnsupportedOperationException | IllegalStateException ignored) {
+ diagnosticsHandler.info(
+ MappingInformationDiagnostics.invalidValueForObjectWithId(lineNumber, FILE_NAME_KEY, ID));
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
new file mode 100644
index 0000000..c1c5105
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -0,0 +1,97 @@
+// 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.mappinginformation;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+public abstract class MappingInformation {
+
+ static final int NO_LINE_NUMBER = -1;
+
+ public static final String MAPPING_ID_KEY = "id";
+
+ private final int lineNumber;
+
+ MappingInformation(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public abstract String serialize();
+
+ public boolean isSignatureMappingInformation() {
+ return false;
+ }
+
+ public SignatureMappingInformation asSignatureMappingInformation() {
+ return null;
+ }
+
+ public boolean isFileNameInformation() {
+ return false;
+ }
+
+ public FileNameInformation asFileNameInformation() {
+ return null;
+ }
+
+ public boolean isMethodSignatureChangedInformation() {
+ return false;
+ }
+
+ public MethodSignatureChangedInformation asMethodSignatureChangedInformation() {
+ return null;
+ }
+
+ public abstract boolean allowOther(MappingInformation information);
+
+ public static MappingInformation fromJsonObject(
+ JsonObject object, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ if (object == null) {
+ diagnosticsHandler.info(MappingInformationDiagnostics.notValidJson(lineNumber));
+ return null;
+ }
+ JsonElement id = object.get(MAPPING_ID_KEY);
+ if (id == null) {
+ diagnosticsHandler.info(
+ MappingInformationDiagnostics.noKeyInJson(lineNumber, MAPPING_ID_KEY));
+ return null;
+ }
+ String idString = id.getAsString();
+ if (idString == null) {
+ diagnosticsHandler.info(
+ MappingInformationDiagnostics.notValidString(lineNumber, MAPPING_ID_KEY));
+ return null;
+ }
+ switch (idString) {
+ case MethodSignatureChangedInformation.ID:
+ return MethodSignatureChangedInformation.build(object, diagnosticsHandler, lineNumber);
+ case FileNameInformation.ID:
+ return FileNameInformation.build(object, diagnosticsHandler, lineNumber);
+ default:
+ diagnosticsHandler.info(MappingInformationDiagnostics.noHandlerFor(lineNumber, idString));
+ return null;
+ }
+ }
+
+ static JsonElement getJsonElementFromObject(
+ JsonObject object,
+ DiagnosticsHandler diagnosticsHandler,
+ int lineNumber,
+ String key,
+ String id) {
+ JsonElement element = object.get(key);
+ if (element == null) {
+ diagnosticsHandler.info(
+ MappingInformationDiagnostics.noKeyForObjectWithId(lineNumber, key, MAPPING_ID_KEY, id));
+ }
+ return element;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java
new file mode 100644
index 0000000..6cc0b28
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java
@@ -0,0 +1,113 @@
+// 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.mappinginformation;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.position.TextPosition;
+
+@Keep
+public class MappingInformationDiagnostics implements Diagnostic {
+
+ private final String message;
+ private final Position position;
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ @Override
+ public Position getPosition() {
+ return position;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return message;
+ }
+
+ private MappingInformationDiagnostics(String message, Position position) {
+ this.message = message;
+ this.position = position;
+ }
+
+ static MappingInformationDiagnostics noHandlerFor(int lineNumber, String value) {
+ return new MappingInformationDiagnostics(
+ String.format("Could not find a handler for %s", value),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics noKeyInJson(int lineNumber, String key) {
+ return new MappingInformationDiagnostics(
+ String.format("Could not locate '%s' in the JSON object", key),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics notValidJson(int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "Not valid JSON", new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics notValidString(int lineNumber, String key) {
+ return new MappingInformationDiagnostics(
+ String.format("The value of '%s' is not a valid string in the JSON object", key),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics tooManyInformationalParameters(int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "More informational parameters than actual parameters for method signature",
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics noKeyForObjectWithId(
+ int lineNumber, String key, String mappingKey, String mappingValue) {
+ return new MappingInformationDiagnostics(
+ String.format("Could not find '%s' for object with %s '%s'", key, mappingKey, mappingValue),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics invalidValueForObjectWithId(
+ int lineNumber, String mappingKey, String mappingValue) {
+ return new MappingInformationDiagnostics(
+ String.format(
+ "Could not decode the information for the object with %s '%s'",
+ mappingKey, mappingValue),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics tooManyEntriesForParameterInformation(int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "Parameter information do not have 1 or 2 entries",
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics invalidParameterInformationObject(int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "Parameter information is not an index and a string representation of a type",
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ public static MappingInformationDiagnostics notAllowedCombination(
+ String className,
+ String renamedClassName,
+ MappingInformation one,
+ MappingInformation other,
+ int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "The mapping '"
+ + one.serialize()
+ + "' is not allowed in combination with '"
+ + other.serialize()
+ + "' in the mapping for "
+ + className
+ + " -> "
+ + renamedClassName,
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MethodSignatureChangedInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MethodSignatureChangedInformation.java
new file mode 100644
index 0000000..8bb023d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MethodSignatureChangedInformation.java
@@ -0,0 +1,258 @@
+// 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.mappinginformation;
+
+import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.invalidParameterInformationObject;
+import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.invalidValueForObjectWithId;
+import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.tooManyEntriesForParameterInformation;
+import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.tooManyInformationalParameters;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * The MethodSignatureChangedInformation structure adds extra information regarding the mapped
+ * method signature that is otherwise not available in the existing proguard mapping format. The
+ * JSON-structure is as follows:
+ *
+ * <pre>
+ * {
+ * "id": "argumentsChanged",
+ * "signature": { methodSignature },
+ * "returnType": "java.lang.String",
+ * "receiver": false,
+ * "params": [
+ * [1], // <-- parameter with original index 1 (starting index based on receiver) is removed.
+ * [2, Foo] // <-- parameter with index 2 has type Foo
+ * ]
+ * }
+ * </pre>
+ */
+public class MethodSignatureChangedInformation extends SignatureMappingInformation {
+
+ private ParameterInformation[] argumentInfos;
+ private final boolean receiver;
+ private final String returnType;
+ private final MethodSignature signature;
+
+ public static final String ID = "methodSignatureChanged";
+ private static final String RETURN_TYPE_KEY = "returnType";
+ private static final String PARAMS_KEY = "params";
+ private static final String RECEIVER_KEY = "receiver";
+
+ @Override
+ public String serialize() {
+ JsonObject result = new JsonObject();
+ serializeMethodSignature(result, signature);
+ result.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
+ result.add(RECEIVER_KEY, new JsonPrimitive(receiver));
+ result.add(RETURN_TYPE_KEY, new JsonPrimitive(returnType));
+ JsonArray arguments = new JsonArray();
+ for (ParameterInformation argInfo : argumentInfos) {
+ arguments.add(argInfo.serialize());
+ }
+ result.add(PARAMS_KEY, arguments);
+ return result.toString();
+ }
+
+ @Override
+ public boolean allowOther(MappingInformation information) {
+ return !information.isMethodSignatureChangedInformation();
+ }
+
+ @Override
+ public Signature getSignature() {
+ return signature;
+ }
+
+ @Override
+ public Signature apply(
+ Signature originalSignature, String renamedName, DiagnosticsHandler diagnosticsHandler) {
+ if (originalSignature == null || !originalSignature.isMethodSignature()) {
+ assert false : "Should only call apply for method signature";
+ return originalSignature;
+ }
+ MethodSignature signature = originalSignature.asMethodSignature();
+ String type = signature.type;
+ String[] parameters = signature.parameters;
+ int numberOfArgumentsRemoved = getNumberOfArgumentsRemoved();
+ if (numberOfArgumentsRemoved > parameters.length) {
+ // The mapping information is not up to date with the current signature.
+ diagnosticsHandler.warning(tooManyInformationalParameters(getLineNumber()));
+ return new MethodSignature(renamedName, type, parameters);
+ }
+ String[] newParameters = new String[parameters.length - numberOfArgumentsRemoved];
+ int insertIndex = 0;
+ for (int i = 0; i < parameters.length; i++) {
+ ParameterInformation argInfo = getParameterInformation(i);
+ if (argInfo != null && argInfo.getType() == null) {
+ // Argument has been removed.
+ } else {
+ if (insertIndex >= newParameters.length) {
+ // The mapping information is not up to date with the current signature.
+ diagnosticsHandler.warning(tooManyInformationalParameters(getLineNumber()));
+ return new MethodSignature(renamedName, type, parameters);
+ } else if (argInfo == null) {
+ // Unchanged, take current parameter.
+ newParameters[insertIndex++] = parameters[i];
+ } else {
+ newParameters[insertIndex++] = argInfo.getType();
+ }
+ }
+ }
+ assert insertIndex == newParameters.length;
+ return new MethodSignature(renamedName, getReturnType(), newParameters);
+ }
+
+ @Override
+ public boolean isMethodSignatureChangedInformation() {
+ return true;
+ }
+
+ public int getNumberOfArgumentsRemoved() {
+ int removedCount = 0;
+ for (ParameterInformation argInfo : argumentInfos) {
+ if (argInfo.type == null) {
+ removedCount++;
+ }
+ }
+ return removedCount;
+ }
+
+ public boolean hasReceiver() {
+ return receiver;
+ }
+
+ public String getReturnType() {
+ return returnType;
+ }
+
+ public ParameterInformation getParameterInformation(int index) {
+ int subtractIndex = receiver ? 1 : 0;
+ for (int i = 0; i < argumentInfos.length; i++) {
+ if (argumentInfos[i].index - subtractIndex == index) {
+ return argumentInfos[i];
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public MethodSignatureChangedInformation asMethodSignatureChangedInformation() {
+ return this;
+ }
+
+ private MethodSignatureChangedInformation(
+ MethodSignature signature,
+ String returnType,
+ boolean hasReceiver,
+ ParameterInformation[] argumentInfos,
+ int lineNumber) {
+ super(lineNumber);
+ this.signature = signature;
+ this.argumentInfos = argumentInfos;
+ this.returnType = returnType;
+ this.receiver = hasReceiver;
+ }
+
+ public static MappingInformation build(
+ JsonObject object, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ try {
+ JsonElement returnTypeElement =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, RETURN_TYPE_KEY, ID);
+ JsonElement receiverElement =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, RECEIVER_KEY, ID);
+ JsonElement argsElement =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, PARAMS_KEY, ID);
+ MethodSignature signature = getMethodSignature(object, ID, diagnosticsHandler, lineNumber);
+ if (signature == null
+ || returnTypeElement == null
+ || receiverElement == null
+ || argsElement == null) {
+ return null;
+ }
+ JsonArray argumentsArray = argsElement.getAsJsonArray();
+ if (argumentsArray == null) {
+ return null;
+ }
+ ParameterInformation[] args = new ParameterInformation[argumentsArray.size()];
+ for (int i = 0; i < argumentsArray.size(); i++) {
+ args[i] =
+ ParameterInformation.fromJsonArray(
+ argumentsArray.get(i).getAsJsonArray(), diagnosticsHandler, lineNumber);
+ }
+ return new MethodSignatureChangedInformation(
+ signature,
+ returnTypeElement.getAsString(),
+ receiverElement.getAsBoolean(),
+ args,
+ lineNumber);
+ } catch (UnsupportedOperationException | IllegalStateException ignored) {
+ diagnosticsHandler.info(invalidValueForObjectWithId(lineNumber, MAPPING_ID_KEY, ID));
+ return null;
+ }
+ }
+
+ public static class ParameterInformation {
+ private final int index;
+ private final String type;
+
+ public int getIndex() {
+ return index;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ private ParameterInformation(int index, String type) {
+ this.index = index;
+ this.type = type;
+ }
+
+ static ParameterInformation fromJsonArray(
+ JsonArray argumentInfo, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ assert argumentInfo != null;
+ try {
+ if (argumentInfo.size() > 2) {
+ diagnosticsHandler.info(tooManyEntriesForParameterInformation(lineNumber));
+ return null;
+ }
+ int index = argumentInfo.get(0).getAsInt();
+ if (argumentInfo.size() == 1) {
+ // This is a removed argument - no type information
+ return new ParameterInformation(index, null);
+ } else {
+ return new ParameterInformation(index, argumentInfo.get(1).getAsString());
+ }
+ } catch (UnsupportedOperationException | IllegalStateException ignored) {
+ diagnosticsHandler.info(invalidParameterInformationObject(lineNumber));
+ return null;
+ }
+ }
+
+ public static ParameterInformation buildRemovedParameterInformation(int index) {
+ return new ParameterInformation(index, null);
+ }
+
+ public static ParameterInformation buildChangedParameterInformation(int index, String type) {
+ return new ParameterInformation(index, type);
+ }
+
+ JsonArray serialize() {
+ JsonArray serializedArray = new JsonArray();
+ serializedArray.add(index);
+ if (type != null) {
+ serializedArray.add(type);
+ }
+ return serializedArray;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/SignatureMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/SignatureMappingInformation.java
new file mode 100644
index 0000000..aba33bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/SignatureMappingInformation.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.mappinginformation;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+public abstract class SignatureMappingInformation extends MappingInformation {
+
+ private static final String SIGNATURE_KEY = "signature";
+
+ SignatureMappingInformation(int lineNumber) {
+ super(lineNumber);
+ }
+
+ @Override
+ public boolean isSignatureMappingInformation() {
+ return true;
+ }
+
+ @Override
+ public SignatureMappingInformation asSignatureMappingInformation() {
+ return this;
+ }
+
+ public abstract Signature getSignature();
+
+ public abstract Signature apply(
+ Signature originalSignature, String renamedName, DiagnosticsHandler diagnosticsHandler);
+
+ JsonObject serializeMethodSignature(JsonObject object, MethodSignature signature) {
+ JsonArray signatureArr = new JsonArray();
+ signatureArr.add(signature.type);
+ signatureArr.add(signature.name);
+ for (String parameter : signature.parameters) {
+ signatureArr.add(parameter);
+ }
+ object.add(SIGNATURE_KEY, signatureArr);
+ return object;
+ }
+
+ static MethodSignature getMethodSignature(
+ JsonObject object, String id, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ JsonElement signatureElement =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, SIGNATURE_KEY, id);
+ if (signatureElement == null || !signatureElement.isJsonArray()) {
+ return null;
+ }
+ // Signature will be [returnType, name, param1, param2, ...].
+ JsonArray signature = signatureElement.getAsJsonArray();
+ String[] parameters = new String[signature.size() - 2];
+ for (int i = 2; i < signature.size(); i++) {
+ parameters[i - 2] = signature.get(i).getAsString();
+ }
+ return new MethodSignature(
+ signature.get(1).getAsString(), signature.get(0).getAsString(), parameters);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 3d59b8b..0cb273e 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -138,7 +138,8 @@
Timing timing = Timing.create("R8 retrace", command.printMemory());
timing.begin("Read proguard map");
ClassNameMapper classNameMapper =
- ClassNameMapper.mapperFromString(command.proguardMapProducer.get());
+ ClassNameMapper.mapperFromString(
+ command.proguardMapProducer.get(), command.diagnosticsHandler);
timing.end();
RetraceBase retraceBase = RetraceBaseImpl.create(classNameMapper);
RetraceCommandLineResult result;
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBase.java b/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
index 2874fc8..ee7d7ab 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
@@ -19,11 +19,5 @@
RetraceTypeResult retrace(TypeReference typeReference);
- String retraceSourceFile(ClassReference classReference, String sourceFile);
-
- String retraceSourceFile(
- ClassReference classReference,
- String sourceFile,
- ClassReference retracedClassReference,
- boolean hasRetraceResult);
+ RetraceSourceFileResult retraceSourceFile(ClassReference classReference, String sourceFile);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java b/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
index 9a68706..1561457 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
@@ -10,15 +10,9 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.Box;
-import com.google.common.collect.Sets;
-import com.google.common.io.Files;
-import java.util.Set;
public class RetraceBaseImpl implements RetraceBase {
- private static final Set<String> UNKNOWN_SOURCEFILE_NAMES =
- Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source");
-
private final ClassNameMapper classNameMapper;
private RetraceBaseImpl(ClassNameMapper classNameMapper) {
@@ -46,10 +40,11 @@
}
@Override
- public String retraceSourceFile(ClassReference classReference, String sourceFile) {
- Box<String> retracedSourceFile = new Box<>();
+ public RetraceSourceFileResult retraceSourceFile(
+ ClassReference classReference, String sourceFile) {
+ Box<RetraceSourceFileResult> retracedSourceFile = new Box<>();
retrace(classReference)
- .forEach(element -> retracedSourceFile.set(element.retraceSourceFile(sourceFile, this)));
+ .forEach(element -> retracedSourceFile.set(element.retraceSourceFile(sourceFile)));
return retracedSourceFile.get();
}
@@ -57,42 +52,4 @@
public RetraceTypeResult retrace(TypeReference typeReference) {
return new RetraceTypeResult(typeReference, this);
}
-
- @Override
- public String retraceSourceFile(
- ClassReference obfuscatedClass,
- String sourceFile,
- ClassReference retracedClassReference,
- boolean hasRetraceResult) {
- boolean fileNameProbablyChanged =
- hasRetraceResult
- && !retracedClassReference.getTypeName().startsWith(obfuscatedClass.getTypeName());
- if (!UNKNOWN_SOURCEFILE_NAMES.contains(sourceFile) && !fileNameProbablyChanged) {
- // We have no new information, only rewrite filename if it is unknown.
- // PG-retrace will always rewrite the filename, but that seems a bit to harsh to do.
- return sourceFile;
- }
- if (!hasRetraceResult) {
- // We have no mapping but but the file name is unknown, so the best we can do is take the
- // name of the obfuscated clazz.
- assert obfuscatedClass.getTypeName().equals(retracedClassReference.getTypeName());
- return getClassSimpleName(obfuscatedClass.getTypeName()) + ".java";
- }
- String newFileName = getClassSimpleName(retracedClassReference.getTypeName());
- String extension = Files.getFileExtension(sourceFile);
- if (extension.isEmpty()) {
- extension = "java";
- }
- return newFileName + "." + extension;
- }
-
- private static String getClassSimpleName(String clazz) {
- int lastIndexOfPeriod = clazz.lastIndexOf('.');
- // Check if we can find a subclass separator.
- int endIndex = clazz.lastIndexOf('$');
- if (lastIndexOfPeriod > endIndex || endIndex < 0) {
- endIndex = clazz.length();
- }
- return clazz.substring(lastIndexOfPeriod + 1, endIndex);
- }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
index 25505fe..a631ddf 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
@@ -4,10 +4,14 @@
package com.android.tools.r8.retrace;
+import static com.android.tools.r8.naming.MemberNaming.NoSignature.NO_SIGNATURE;
+import static com.android.tools.r8.retrace.RetraceUtils.synthesizeFileName;
+
import com.android.tools.r8.Keep;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.RetraceClassResult.Element;
@@ -122,9 +126,26 @@
return classResult;
}
- public String retraceSourceFile(String fileName, RetraceBase retraceBase) {
- return retraceBase.retraceSourceFile(
- classResult.obfuscatedReference, fileName, classReference, mapper != null);
+ public RetraceSourceFileResult retraceSourceFile(String sourceFile) {
+ if (mapper != null && mapper.getAdditionalMappings().size() > 0) {
+ List<MappingInformation> mappingInformations =
+ mapper.getAdditionalMappings().get(NO_SIGNATURE);
+ if (mappingInformations != null) {
+ for (MappingInformation mappingInformation : mappingInformations) {
+ if (mappingInformation.isFileNameInformation()) {
+ return new RetraceSourceFileResult(
+ mappingInformation.asFileNameInformation().getFileName(), false);
+ }
+ }
+ }
+ }
+ return new RetraceSourceFileResult(
+ synthesizeFileName(
+ classReference.getTypeName(),
+ classResult.obfuscatedReference.getTypeName(),
+ sourceFile,
+ mapper != null),
+ true);
}
public RetraceFieldResult lookupField(String fieldName) {
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
index f0719ea..ff1af91 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
@@ -180,5 +180,9 @@
}
return mappedRange.getFirstLineNumberOfOriginalRange();
}
+
+ public RetraceSourceFileResult retraceSourceFile(String sourceFile) {
+ return RetraceUtils.getSourceFile(classElement, methodReference.getHolderClass(), sourceFile);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
index f1603f7..e2e78b2 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
@@ -671,20 +671,21 @@
retracedStrings.add(retraceString);
continue;
}
- String newSourceFile =
- retraceString.getQualifiedContext() != null
- ? retraceBase.retraceSourceFile(
- retraceString.classContext.getClassReference(),
- fileName,
+ RetraceSourceFileResult sourceFileResult =
+ retraceString.getMethodContext() != null
+ ? retraceString.getMethodContext().retraceSourceFile(fileName)
+ : RetraceUtils.getSourceFile(
+ retraceString.getClassContext(),
retraceString.getQualifiedContext(),
- true)
- : retraceString.classContext.retraceSourceFile(fileName, retraceBase);
+ fileName);
retracedStrings.add(
retraceString
.transform()
.setSource(fileName)
.replaceInString(
- newSourceFile, matcher.start(captureGroup), matcher.end(captureGroup))
+ sourceFileResult.getFilename(),
+ matcher.start(captureGroup),
+ matcher.end(captureGroup))
.build());
}
return retracedStrings;
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java
new file mode 100644
index 0000000..4b21be1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public class RetraceSourceFileResult {
+
+ private final String filename;
+ private final boolean synthesized;
+
+ RetraceSourceFileResult(String filename, boolean synthesized) {
+ this.filename = filename;
+ this.synthesized = synthesized;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public boolean isSynthesized() {
+ return synthesized;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
index c63a707..719dd44 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
@@ -421,7 +421,8 @@
List<StackTraceLine> lines,
String classLoaderName) {
ClassReference classReference = Reference.classFromTypeName(clazz);
- RetraceMethodResult retraceResult = retraceBase.retrace(classReference).lookupMethod(method);
+ RetraceClassResult classResult = retraceBase.retrace(classReference);
+ RetraceMethodResult retraceResult = classResult.lookupMethod(method);
if (linePosition != NO_POSITION && linePosition != INVALID_POSITION) {
retraceResult = retraceResult.narrowByLine(linePosition);
}
@@ -437,8 +438,7 @@
methodReference.getHolderClass().getTypeName(),
methodReference.getMethodName(),
methodDescriptionFromMethodReference(methodReference, verbose),
- retraceBase.retraceSourceFile(
- classReference, fileName, methodReference.getHolderClass(), true),
+ methodElement.retraceSourceFile(fileName).getFilename(),
hasLinePosition()
? methodElement.getOriginalLineNumber(linePosition)
: linePosition,
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
index 81c2272..c82bf36 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
@@ -4,11 +4,18 @@
package com.android.tools.r8.retrace;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import java.util.Set;
public class RetraceUtils {
+ private static final Set<String> UNKNOWN_SOURCEFILE_NAMES =
+ Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source");
+
public static String methodDescriptionFromMethodReference(
MethodReference methodReference, boolean verbose) {
if (!verbose || methodReference.isUnknown()) {
@@ -35,4 +42,64 @@
sb.append(")");
return sb.toString();
}
+
+ public static boolean hasPredictableSourceFileName(String originalClassName, String sourceFile) {
+ String synthesizedSourceFileName = getClassSimpleName(originalClassName) + ".java";
+ return synthesizedSourceFileName.equals(sourceFile);
+ }
+
+ private static String getClassSimpleName(String clazz) {
+ int lastIndexOfPeriod = clazz.lastIndexOf('.');
+ // Check if we can find a subclass separator.
+ int endIndex = clazz.lastIndexOf('$');
+ if (lastIndexOfPeriod > endIndex || endIndex < 0) {
+ endIndex = clazz.length();
+ }
+ return clazz.substring(lastIndexOfPeriod + 1, endIndex);
+ }
+
+ static RetraceSourceFileResult getSourceFile(
+ RetraceClassResult.Element classElement, ClassReference context, String sourceFile) {
+ // For inline frames we do not have the class element associated with it.
+ if (context == null) {
+ return classElement.retraceSourceFile(sourceFile);
+ }
+ if (context.equals(classElement.getClassReference())) {
+ return classElement.retraceSourceFile(sourceFile);
+ } else {
+ return new RetraceSourceFileResult(
+ synthesizeFileName(
+ context.getTypeName(),
+ classElement.getClassReference().getTypeName(),
+ sourceFile,
+ true),
+ true);
+ }
+ }
+
+ public static String synthesizeFileName(
+ String retracedClassName,
+ String minifiedClassName,
+ String sourceFile,
+ boolean hasRetraceResult) {
+ boolean fileNameProbablyChanged =
+ hasRetraceResult && !retracedClassName.startsWith(minifiedClassName);
+ if (!UNKNOWN_SOURCEFILE_NAMES.contains(sourceFile) && !fileNameProbablyChanged) {
+ // We have no new information, only rewrite filename if it is unknown.
+ // PG-retrace will always rewrite the filename, but that seems a bit to harsh to do.
+ return sourceFile;
+ }
+ String extension = Files.getFileExtension(sourceFile);
+ if (extension.isEmpty()) {
+ extension = "java";
+ }
+ if (!hasRetraceResult) {
+ // We have no mapping but but file name is unknown, so the best we can do is take the
+ // name of the obfuscated clazz.
+ assert minifiedClassName.equals(retracedClassName);
+ return getClassSimpleName(minifiedClassName) + "." + extension;
+ }
+ String newFileName = getClassSimpleName(retracedClassName);
+ return newFileName + "." + extension;
+ }
}
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 e033e78..5c0f735 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -57,6 +57,7 @@
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.LookupLambdaTarget;
import com.android.tools.r8.graph.LookupTarget;
@@ -1883,21 +1884,27 @@
}
}
+ private void shouldNotBeMinified(DexReference reference) {
+ if (options.isMinificationEnabled()) {
+ rootSet.shouldNotBeMinified(reference);
+ }
+ }
+
private void keepClassAndAllMembers(DexProgramClass clazz, KeepReason keepReason) {
KeepReasonWitness keepReasonWitness = graphReporter.registerClass(clazz, keepReason);
markClassAsInstantiatedWithCompatRule(clazz.asProgramClass(), keepReasonWitness);
keepInfo.keepClass(clazz);
- rootSet.shouldNotBeMinified(clazz.toReference());
+ shouldNotBeMinified(clazz.toReference());
clazz.forEachProgramField(
field -> {
keepInfo.keepField(field);
- rootSet.shouldNotBeMinified(field.getReference());
+ shouldNotBeMinified(field.getReference());
markFieldAsKept(field, keepReasonWitness);
});
clazz.forEachProgramMethod(
method -> {
keepInfo.keepMethod(method);
- rootSet.shouldNotBeMinified(method.getReference());
+ shouldNotBeMinified(method.getReference());
markMethodAsKept(method, keepReasonWitness);
});
}
@@ -2593,7 +2600,7 @@
// marking for not renaming it is in the root set.
workList.enqueueMarkMethodKeptAction(new ProgramMethod(clazz, valuesMethod), reason);
keepInfo.keepMethod(clazz, valuesMethod);
- rootSet.shouldNotBeMinified(valuesMethod.toReference());
+ shouldNotBeMinified(valuesMethod.toReference());
}
}
@@ -2672,6 +2679,7 @@
// This is simulating the effect of the "root set" applied rules.
// This is done only in the initial pass, in subsequent passes the "rules" are reapplied
// by iterating the instances.
+ assert appView.options().isMinificationEnabled() || rootSet.noObfuscation.isEmpty();
for (DexReference reference : rootSet.noObfuscation) {
keepInfo.evaluateRule(reference, appInfo, Joiner::disallowMinification);
}
@@ -2715,6 +2723,12 @@
return appInfoWithLiveness;
}
+ public GraphLense buildGraphLense(AppView<?> appView) {
+ return lambdaRewriter != null
+ ? lambdaRewriter.buildMappingLense(appView)
+ : appView.graphLense();
+ }
+
private void keepClassWithRules(DexProgramClass clazz, Set<ProguardKeepRuleBase> rules) {
keepInfo.joinClass(clazz, info -> applyKeepRules(rules, info));
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index 305c108..326a95e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -149,7 +149,10 @@
builder.matchAllSpecification();
builder.setType(ProguardKeepRuleType.KEEP);
modifiers.accept(builder.getModifiersBuilder());
- return builder.build();
+ ProguardKeepRule rule = builder.build();
+ // Consider the default keep all rule as always matched.
+ rule.markAsUsed();
+ return rule;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index ec9f101..08830cc 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -326,6 +326,7 @@
assert Sets.intersection(neverInline, alwaysInline).isEmpty()
&& Sets.intersection(neverInline, forceInline).isEmpty()
: "A method cannot be marked as both -neverinline and -forceinline/-alwaysinline.";
+ assert appView.options().isMinificationEnabled() || noObfuscation.isEmpty();
return new RootSet(
noShrinking,
noObfuscation,
@@ -1035,7 +1036,9 @@
.computeIfAbsent(item.toReference(), x -> new MutableItemsWithRules())
.addClassWithRule(type, context);
// Unconditionally add to no-obfuscation, as that is only checked for surviving items.
- noObfuscation.add(type);
+ if (appView.options().isMinificationEnabled()) {
+ noObfuscation.add(type);
+ }
}
private void includeDescriptorClasses(DexDefinition item, ProguardKeepRuleBase context) {
@@ -1133,7 +1136,7 @@
context.markAsUsed();
}
- if (!modifiers.allowsObfuscation) {
+ if (appView.options().isMinificationEnabled() && !modifiers.allowsObfuscation) {
noObfuscation.add(item.toReference());
context.markAsUsed();
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 8599b02..9dd4865 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -472,7 +472,13 @@
}
private String getBuildPropertiesContents(InternalOptions options) {
- return "min-api=" + options.minApiLevel;
+ return String.join(
+ "\n",
+ ImmutableList.of(
+ "mode=" + (options.debug ? "debug" : "release"),
+ "min-api=" + options.minApiLevel,
+ "tree-shaking=" + options.isTreeShakingEnabled(),
+ "minification=" + options.isMinificationEnabled()));
}
private int dumpLibraryResources(int nextDexIndex, ZipOutputStream out)
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
index 936395f..e349c9a 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
import com.android.tools.r8.R8Command.Builder;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -17,6 +18,8 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
/**
* Wrapper to make it easy to call R8 in compat mode when compiling a dump file.
@@ -44,7 +47,8 @@
"--main-dex-list-output",
"--pg-conf",
"--pg-map-output",
- "--desugared-lib");
+ "--desugared-lib",
+ "--threads");
private static final List<String> VALID_OPTIONS_WITH_TWO_OPERANDS =
Arrays.asList("--feature-jar");
@@ -69,6 +73,7 @@
List<Path> classpath = new ArrayList<>();
List<Path> config = new ArrayList<>();
int minApi = 1;
+ int threads = -1;
for (int i = 0; i < args.length; i++) {
String option = args[i];
if (VALID_OPTIONS.contains(option)) {
@@ -129,6 +134,11 @@
pgMapOutput = Paths.get(operand);
break;
}
+ case "--threads":
+ {
+ threads = Integer.parseInt(operand);
+ break;
+ }
default:
throw new IllegalArgumentException("Unimplemented option: " + option);
}
@@ -174,6 +184,16 @@
if (pgMapOutput != null) {
commandBuilder.setProguardMapOutputPath(pgMapOutput);
}
- R8.run(commandBuilder.build());
+ R8Command command = commandBuilder.build();
+ if (threads != -1) {
+ ExecutorService executor = Executors.newWorkStealingPool(threads);
+ try {
+ R8.run(command, executor);
+ } finally {
+ executor.shutdown();
+ }
+ } else {
+ R8.run(command);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 83f8ec0..6907805 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -47,6 +47,8 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.Range;
+import com.android.tools.r8.naming.mappinginformation.FileNameInformation;
+import com.android.tools.r8.retrace.RetraceUtils;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.google.common.base.Suppliers;
@@ -287,6 +289,15 @@
originalType.toSourceString(),
com.android.tools.r8.position.Position.UNKNOWN));
+ // Check if source file should be added to the map
+ if (clazz.sourceFile != null) {
+ String sourceFile = clazz.sourceFile.toString();
+ if (!RetraceUtils.hasPredictableSourceFileName(clazz.toSourceString(), sourceFile)) {
+ Builder builder = onDemandClassNamingBuilder.get();
+ builder.addMappingInformation(FileNameInformation.build(sourceFile));
+ }
+ }
+
// If the class is renamed add it to the classNamingBuilder.
addClassToClassNaming(originalType, renamedClassName, onDemandClassNamingBuilder);
@@ -367,12 +378,8 @@
signatures.put(originalMethod, originalSignature);
Function<DexMethod, MethodSignature> getOriginalMethodSignature =
m -> {
- DexMethod original = appView.graphLense().getOriginalMethodSignature(m);
return signatures.computeIfAbsent(
- original,
- key ->
- MethodSignature.fromDexMethod(
- original, original.holder != clazz.getType()));
+ m, key -> MethodSignature.fromDexMethod(m, m.holder != clazz.getType()));
};
MemberNaming memberNaming = new MemberNaming(originalSignature, obfuscatedName);
diff --git a/src/test/java/com/android/tools/r8/MarkersTest.java b/src/test/java/com/android/tools/r8/MarkersTest.java
index cc7b81e..f2e539b 100644
--- a/src/test/java/com/android/tools/r8/MarkersTest.java
+++ b/src/test/java/com/android/tools/r8/MarkersTest.java
@@ -17,7 +17,10 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
+import com.google.gson.JsonParser;
import java.nio.file.Path;
import java.util.Collection;
import org.hamcrest.Matcher;
@@ -65,11 +68,18 @@
}
L8.run(builder.build());
Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(output);
+ String version =
+ new JsonParser()
+ .parse(FileUtils.readTextFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING, Charsets.UTF_8))
+ .getAsJsonObject()
+ .get("version")
+ .getAsString();
+
Matcher<Marker> l8Matcher =
allOf(
markerTool(Tool.L8),
markerCompilationMode(compilationMode),
- markerDesugaredLibraryIdentifier("com.tools.android:desugar_jdk_libs:1.0.10"),
+ markerDesugaredLibraryIdentifier("com.tools.android:desugar_jdk_libs:" + version),
markerHasChecksums(false));
Matcher<Marker> d8Matcher =
allOf(
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index b262e4e..f53e8b3 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
@@ -154,7 +155,7 @@
return addKeepMainRule(mainClass.getTypeName());
}
- public T addKeepMainRules(Class<?>[] mainClasses) {
+ public T addKeepMainRules(Class<?>... mainClasses) {
for (Class<?> mainClass : mainClasses) {
this.addKeepMainRule(mainClass);
}
@@ -210,11 +211,15 @@
}
public T addKeepAttributeLineNumberTable() {
- return addKeepAttributes("LineNumberTable");
+ return addKeepAttributes(ProguardKeepAttributes.LINE_NUMBER_TABLE);
+ }
+
+ public T addKeepAttributeSourceFile() {
+ return addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE);
}
public T addKeepRuntimeVisibleAnnotations() {
- return addKeepAttributes("RuntimeVisibleAnnotations");
+ return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS);
}
public T addKeepAllAttributes() {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
index 1141cb3..12ed820 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
@@ -299,9 +299,13 @@
} else if (element instanceof Integer) {
builder.append(element);
} else if (element instanceof List) {
- toJsonList((List<Object>) element, builder);
+ @SuppressWarnings("unchecked")
+ List<Object> elements = (List<Object>) element;
+ toJsonList(elements, builder);
} else if (element instanceof Map) {
- toJsonObject((Map<String, Object>) element, builder);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> elements = (Map<String, Object>) element;
+ toJsonObject(elements, builder);
} else {
throw new IllegalStateException("Unexpected object type: " + element.getClass());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
index ad8e74c..fcc2124 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
@@ -30,8 +30,7 @@
private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
private static final String EXPECTED_RESULT =
- StringUtils.lines(
- "[5, 6, 7]", "j$.$r8$wrapper$java$util$stream$IntStream$-V-WRP", "IntSummaryStatistics");
+ StringUtils.lines("[5, 6, 7]", "j$.$r8$wrapper$java$util$stream$IntStream$-V-WRP");
@Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
public static List<Object[]> data() {
@@ -75,7 +74,11 @@
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Executor.class)
- .assertSuccessWithOutput(EXPECTED_RESULT);
+ .assertFailureWithOutput(EXPECTED_RESULT)
+ .assertFailureWithErrorThatMatches(
+ containsString(
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " from java.util.IntSummaryStatistics"));
}
@Test
@@ -95,7 +98,11 @@
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Executor.class)
- .assertSuccessWithOutput(EXPECTED_RESULT);
+ .assertFailureWithOutput(EXPECTED_RESULT)
+ .assertFailureWithErrorThatMatches(
+ containsString(
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " from java.util.IntSummaryStatistics"));
}
static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
index 21074c9..7fe00d5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
@@ -29,6 +29,16 @@
private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
private static final String EXPECTED_RESULT =
StringUtils.lines(
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " from java.util.IntSummaryStatistics",
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " to java.util.LongSummaryStatistics",
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " to java.util.IntSummaryStatistics",
+ "Java 8+ API desugaring (library desugaring) cannot convert"
+ + " to java.util.DoubleSummaryStatistics");
+ private static final String SUCCESS_EXPECTED_RESULT =
+ StringUtils.lines(
"2", "1", "42", "42", "42", "1", "42", "42", "42", "1", "42.0", "42.0", "42.0");
private static Path CUSTOM_LIB;
@@ -105,41 +115,58 @@
}
private static void realTest() {
- System.out.println("foo".subSequence(0, 2).codePoints().summaryStatistics().getCount());
+ try {
+ System.out.println("foo".subSequence(0, 2).codePoints().summaryStatistics().getCount());
+ } catch (Error e) {
+ System.out.println(e.getMessage());
+ }
}
public static void longTest() {
long[] longs = new long[1];
longs[0] = 42L;
- LongSummaryStatistics mix =
- CustomLibClass.mix(Arrays.stream(longs).summaryStatistics(), new LongSummaryStatistics());
- System.out.println(mix.getCount());
- System.out.println(mix.getMin());
- System.out.println(mix.getMax());
- System.out.println(mix.getSum());
+ try {
+ LongSummaryStatistics mix =
+ CustomLibClass.mix(
+ Arrays.stream(longs).summaryStatistics(), new LongSummaryStatistics());
+ System.out.println(mix.getCount());
+ System.out.println(mix.getMin());
+ System.out.println(mix.getMax());
+ System.out.println(mix.getSum());
+ } catch (Error e) {
+ System.out.println(e.getMessage());
+ }
}
public static void intTest() {
int[] ints = new int[1];
ints[0] = 42;
- IntSummaryStatistics mix =
- CustomLibClass.mix(Arrays.stream(ints).summaryStatistics(), new IntSummaryStatistics());
- System.out.println(mix.getCount());
- System.out.println(mix.getMin());
- System.out.println(mix.getMax());
- System.out.println(mix.getSum());
+ try {
+ IntSummaryStatistics mix =
+ CustomLibClass.mix(Arrays.stream(ints).summaryStatistics(), new IntSummaryStatistics());
+ System.out.println(mix.getCount());
+ System.out.println(mix.getMin());
+ System.out.println(mix.getMax());
+ System.out.println(mix.getSum());
+ } catch (Error e) {
+ System.out.println(e.getMessage());
+ }
}
public static void doubleTest() {
double[] doubles = new double[1];
doubles[0] = 42L;
- DoubleSummaryStatistics mix =
- CustomLibClass.mix(
- Arrays.stream(doubles).summaryStatistics(), new DoubleSummaryStatistics());
- System.out.println(mix.getCount());
- System.out.println(mix.getMin());
- System.out.println(mix.getMax());
- System.out.println(mix.getSum());
+ try {
+ DoubleSummaryStatistics mix =
+ CustomLibClass.mix(
+ Arrays.stream(doubles).summaryStatistics(), new DoubleSummaryStatistics());
+ System.out.println(mix.getCount());
+ System.out.println(mix.getMin());
+ System.out.println(mix.getMax());
+ System.out.println(mix.getSum());
+ } catch (Error e) {
+ System.out.println(e.getMessage());
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
index 1cf4e93..6483aee 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
@@ -23,7 +23,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -31,7 +31,7 @@
}
public AnnotationEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -46,7 +46,7 @@
.addInnerClasses(AnnotationEnumUnboxingTest.class)
.noMinification()
.addKeepMainRule(Main.class)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addKeepClassRules(ClassAnnotationDefault.class)
.addKeepRuntimeVisibleAnnotations()
.enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ComparisonEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ComparisonEnumUnboxingTest.java
index 19c069d..9873711 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ComparisonEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ComparisonEnumUnboxingTest.java
@@ -23,7 +23,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -31,7 +31,7 @@
}
public ComparisonEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -45,7 +45,7 @@
.addKeepMainRules(INPUTS)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
index 7582177..d1e5b4a 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
@@ -28,7 +28,7 @@
public class DoubleProcessingEnumUnboxingTest extends EnumUnboxingTestBase {
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
private final boolean minification;
@Parameters(name = "{0} valueOpt: {1} keep: {2} minif: {3}")
@@ -43,7 +43,7 @@
public DoubleProcessingEnumUnboxingTest(
TestParameters parameters,
boolean enumValueOptimization,
- KeepRule enumKeepRules,
+ EnumKeepRules enumKeepRules,
boolean minification) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
@@ -59,7 +59,7 @@
.addProgramClasses(JavaLibrary1.class, JavaLibrary1.LibEnum1.class)
.addKeepMethodRules(
Reference.methodFromMethod(JavaLibrary1.class.getDeclaredMethod("libCall")))
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.minification(minification)
@@ -72,7 +72,7 @@
.addProgramClasses(App.class, App.AppEnum.class)
.addProgramFiles(javaLibShrunk)
.addKeepMainRule(App.class)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
index e5f3913..49fc118 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
@@ -29,7 +29,7 @@
public class DoubleProcessingMergeEnumUnboxingTest extends EnumUnboxingTestBase {
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
private final boolean minification;
@Parameters(name = "{0} valueOpt: {1} keep: {2} minif: {3}")
@@ -44,7 +44,7 @@
public DoubleProcessingMergeEnumUnboxingTest(
TestParameters parameters,
boolean enumValueOptimization,
- KeepRule enumKeepRules,
+ EnumKeepRules enumKeepRules,
boolean minification) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
@@ -63,7 +63,7 @@
.addProgramClasses(App.class, App.AppEnum.class)
.addProgramFiles(javaLibShrunk1, javaLibShrunk2)
.addKeepMainRule(App.class)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
@@ -82,7 +82,7 @@
return testForR8(Backend.CF)
.addProgramClasses(libClass, enumLibClass)
.addKeepMethodRules(Reference.methodFromMethod(libClass.getDeclaredMethod("libCall")))
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.minification(minification)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumClinitWithSideEffectsUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumClinitWithSideEffectsUnboxingTest.java
index c3e548e..92aa76e 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumClinitWithSideEffectsUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumClinitWithSideEffectsUnboxingTest.java
@@ -10,7 +10,7 @@
@RunWith(Parameterized.class)
public class EnumClinitWithSideEffectsUnboxingTest extends EnumUnboxingTestBase {
- private final KeepRule enumKeepRule;
+ private final EnumKeepRules enumEnumKeepRules;
private final TestParameters parameters;
@Parameters(name = "{1}, enum keep rule: {0}")
@@ -19,8 +19,9 @@
getAllEnumKeepRules(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public EnumClinitWithSideEffectsUnboxingTest(KeepRule enumKeepRule, TestParameters parameters) {
- this.enumKeepRule = enumKeepRule;
+ public EnumClinitWithSideEffectsUnboxingTest(
+ EnumKeepRules enumEnumKeepRules, TestParameters parameters) {
+ this.enumEnumKeepRules = enumEnumKeepRules;
this.parameters = parameters;
}
@@ -29,7 +30,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(EnumClinitWithSideEffectsUnboxingTest.class)
.addKeepMainRule(TestClass.class)
- .addKeepRules(enumKeepRule.getKeepRule())
+ .addKeepRules(enumEnumKeepRules.getKeepRules())
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumInitWithSideEffectsUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumInitWithSideEffectsUnboxingTest.java
index b9445c2..c3967e5 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumInitWithSideEffectsUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumInitWithSideEffectsUnboxingTest.java
@@ -10,7 +10,7 @@
@RunWith(Parameterized.class)
public class EnumInitWithSideEffectsUnboxingTest extends EnumUnboxingTestBase {
- private final KeepRule enumKeepRule;
+ private final EnumKeepRules enumEnumKeepRules;
private final TestParameters parameters;
@Parameters(name = "{1}, enum keep rule: {0}")
@@ -19,8 +19,9 @@
getAllEnumKeepRules(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public EnumInitWithSideEffectsUnboxingTest(KeepRule enumKeepRule, TestParameters parameters) {
- this.enumKeepRule = enumKeepRule;
+ public EnumInitWithSideEffectsUnboxingTest(
+ EnumKeepRules enumEnumKeepRules, TestParameters parameters) {
+ this.enumEnumKeepRules = enumEnumKeepRules;
this.parameters = parameters;
}
@@ -29,7 +30,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(EnumInitWithSideEffectsUnboxingTest.class)
.addKeepMainRule(TestClass.class)
- .addKeepRules(enumKeepRule.getKeepRule())
+ .addKeepRules(enumEnumKeepRules.getKeepRules())
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
new file mode 100644
index 0000000..83393bf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
@@ -0,0 +1,218 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class EnumToStringLibTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final EnumKeepRules enumKeepRules;
+ private final boolean enumUnboxing;
+
+ @Parameterized.Parameters(name = "{0} valueOpt: {1} keep: {2} unbox: {3}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ BooleanUtils.values(),
+ getAllEnumKeepRules(),
+ BooleanUtils.values());
+ }
+
+ public EnumToStringLibTest(
+ TestParameters parameters,
+ boolean enumValueOptimization,
+ EnumKeepRules enumKeepRules,
+ boolean enumUnboxing) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ this.enumUnboxing = enumUnboxing;
+ }
+
+ @Test
+ public void testToStringLib() throws Exception {
+ Assume.assumeFalse(
+ "The test rely on valueOf, so only studio or snap keep rules are valid.",
+ enumKeepRules == EnumKeepRules.NONE);
+ // Compile the lib cf to cf.
+ R8TestCompileResult javaLibShrunk = compileLibrary();
+ assertEnumFieldsMinified(javaLibShrunk.inspector());
+ // Compile the program with the lib.
+ R8TestCompileResult compile =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(AlwaysCorrectProgram.class, AlwaysCorrectProgram2.class)
+ .addProgramFiles(javaLibShrunk.writeToZip())
+ .addKeepMainRule(AlwaysCorrectProgram.class)
+ .addKeepMainRule(AlwaysCorrectProgram2.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(
+ options -> {
+ options.enableEnumUnboxing = enumUnboxing;
+ options.enableEnumValueOptimization = enumValueOptimization;
+ options.enableEnumSwitchMapRemoval = enumValueOptimization;
+ options.testing.enableEnumUnboxingDebugLogs = enumUnboxing;
+ })
+ .allowDiagnosticInfoMessages(enumUnboxing)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ compile
+ .run(parameters.getRuntime(), AlwaysCorrectProgram.class)
+ .assertSuccessWithOutputLines("0", "1", "2", "0", "1", "2", "0", "1", "2");
+ if (!enumKeepRules.isSnap() && enumUnboxing) {
+ // TODO(b/160667929): Fix toString and enum unboxing.
+ compile
+ .run(parameters.getRuntime(), AlwaysCorrectProgram2.class)
+ .assertFailureWithErrorThatMatches(containsString("IllegalArgumentException"));
+ return;
+ }
+ compile
+ .run(parameters.getRuntime(), AlwaysCorrectProgram2.class)
+ .assertSuccessWithOutputLines("0", "1", "2", "0", "1", "2");
+ }
+
+ private void assertEnumFieldsMinified(CodeInspector codeInspector) throws Exception {
+ if (enumKeepRules.isSnap()) {
+ return;
+ }
+ ClassSubject clazz = codeInspector.clazz(ToStringLib.LibEnum.class);
+ assertThat(clazz, isPresent());
+ for (String fieldName : new String[] {"COFFEE", "BEAN", "SUGAR"}) {
+ assertThat(
+ codeInspector.field(ToStringLib.LibEnum.class.getField(fieldName)),
+ isPresentAndRenamed());
+ }
+ }
+
+ private R8TestCompileResult compileLibrary() throws Exception {
+ return testForR8(Backend.CF)
+ .addProgramClasses(ToStringLib.class, ToStringLib.LibEnum.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ // TODO(b/160535629): Work-around on some optimizations relying on $VALUES name.
+ .addKeepRules(
+ "-keep enum "
+ + ToStringLib.LibEnum.class.getName()
+ + " { static "
+ + ToStringLib.LibEnum.class.getName()
+ + "[] $VALUES; }")
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .addKeepMethodRules(
+ Reference.methodFromMethod(
+ ToStringLib.class.getDeclaredMethod("lookupByName", String.class)),
+ Reference.methodFromMethod(ToStringLib.class.getDeclaredMethod("getCoffee")),
+ Reference.methodFromMethod(ToStringLib.class.getDeclaredMethod("getBean")),
+ Reference.methodFromMethod(ToStringLib.class.getDeclaredMethod("getSugar")),
+ Reference.methodFromMethod(ToStringLib.class.getDeclaredMethod("directCoffee")),
+ Reference.methodFromMethod(ToStringLib.class.getDeclaredMethod("directBean")),
+ Reference.methodFromMethod(ToStringLib.class.getDeclaredMethod("directSugar")))
+ .addKeepClassRules(ToStringLib.LibEnum.class)
+ .allowDiagnosticMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ msg ->
+ assertEnumIsBoxed(
+ ToStringLib.LibEnum.class, ToStringLib.LibEnum.class.getSimpleName(), msg));
+ }
+
+ // This class emulates a library with the three public methods getEnumXXX.
+ public static class ToStringLib {
+
+ // We pick names here that we assume won't be picked by the minifier (i.e., not a,b,c).
+ public enum LibEnum {
+ COFFEE,
+ BEAN,
+ SUGAR;
+ }
+
+ // If there is a keep rule on LibEnum fields, then ToStringLib.lookupByName("COFFEE")
+ // should answer 0, else, the behavior of ToStringLib.lookupByName("COFFEE") is undefined.
+ // ToStringLib.lookupByName(LibEnum.COFFEE.toString()) should always answer 0, no matter
+ // what keep rules are on LibEnum.
+ public static int lookupByName(String key) {
+ if (key == null) {
+ return -1;
+ } else if (key.contains(LibEnum.COFFEE.name())) {
+ return LibEnum.COFFEE.ordinal();
+ } else if (key.contains(LibEnum.BEAN.name())) {
+ return LibEnum.BEAN.ordinal();
+ } else if (key.contains(LibEnum.SUGAR.name())) {
+ return LibEnum.SUGAR.ordinal();
+ } else {
+ return -2;
+ }
+ }
+
+ // The following method should always return 0, no matter what keep rules are on LibEnum.
+ public static int directCoffee() {
+ return LibEnum.valueOf(LibEnum.COFFEE.toString()).ordinal();
+ }
+
+ public static int directBean() {
+ return LibEnum.valueOf(LibEnum.BEAN.toString()).ordinal();
+ }
+
+ public static int directSugar() {
+ return LibEnum.valueOf(LibEnum.SUGAR.toString()).ordinal();
+ }
+
+ public static LibEnum getCoffee() {
+ return LibEnum.COFFEE;
+ }
+
+ public static LibEnum getBean() {
+ return LibEnum.BEAN;
+ }
+
+ public static LibEnum getSugar() {
+ return LibEnum.SUGAR;
+ }
+ }
+
+ // The next two classes emulate a program using the ToStringLib library.
+ public static class AlwaysCorrectProgram {
+
+ public static void main(String[] args) {
+ System.out.println(ToStringLib.directCoffee());
+ System.out.println(ToStringLib.directBean());
+ System.out.println(ToStringLib.directSugar());
+ System.out.println(ToStringLib.lookupByName(ToStringLib.getCoffee().toString()));
+ System.out.println(ToStringLib.lookupByName(ToStringLib.getBean().toString()));
+ System.out.println(ToStringLib.lookupByName(ToStringLib.getSugar().toString()));
+ System.out.println(ToStringLib.LibEnum.valueOf(ToStringLib.getCoffee().toString()).ordinal());
+ System.out.println(ToStringLib.LibEnum.valueOf(ToStringLib.getBean().toString()).ordinal());
+ System.out.println(ToStringLib.LibEnum.valueOf(ToStringLib.getSugar().toString()).ordinal());
+ }
+ }
+
+ public static class AlwaysCorrectProgram2 {
+
+ public static void main(String[] args) {
+ System.out.println(ToStringLib.lookupByName("COFFEE"));
+ System.out.println(ToStringLib.lookupByName("BEAN"));
+ System.out.println(ToStringLib.lookupByName("SUGAR"));
+ System.out.println(ToStringLib.LibEnum.valueOf("COFFEE").ordinal());
+ System.out.println(ToStringLib.LibEnum.valueOf("BEAN").ordinal());
+ System.out.println(ToStringLib.LibEnum.valueOf("SUGAR").ordinal());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java
index d9c9350..d4b73ff 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java
@@ -28,7 +28,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -36,7 +36,7 @@
}
public EnumUnboxingArrayTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -50,7 +50,7 @@
.addKeepMainRules(SUCCESSES)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
new file mode 100644
index 0000000..5ccd903
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class EnumUnboxingB160535628Test extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private final boolean missingStaticMethods;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(), BooleanUtils.values());
+ }
+
+ public EnumUnboxingB160535628Test(TestParameters parameters, boolean missingStaticMethods) {
+ this.parameters = parameters;
+ this.missingStaticMethods = missingStaticMethods;
+ }
+
+ @Test
+ public void testCallToMissingStaticMethodInUnboxedEnum() throws Exception {
+ // Compile the lib cf to cf.
+ Path javaLibShrunk = compileLibrary();
+ // Compile the program with the lib.
+ // This should compile without error into code raising the correct NoSuchMethod errors.
+ R8TestCompileResult compile =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(ProgramValueOf.class, ProgramStaticMethod.class)
+ .addProgramFiles(javaLibShrunk)
+ .addKeepMainRules(ProgramValueOf.class, ProgramStaticMethod.class)
+ .addOptionsModification(
+ options -> {
+ options.enableEnumUnboxing = true;
+ options.testing.enableEnumUnboxingDebugLogs = true;
+ })
+ .allowDiagnosticMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ // The enums cannot be unboxed if static methods are missing,
+ // but they should be unboxed otherwise.
+ this::assertEnumUnboxedIfStaticMethodsPresent);
+ if (missingStaticMethods) {
+ compile
+ .run(parameters.getRuntime(), ProgramValueOf.class)
+ .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError"))
+ .assertFailureWithErrorThatMatches(containsString("valueOf"));
+ compile
+ .run(parameters.getRuntime(), ProgramStaticMethod.class)
+ .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError"))
+ .assertFailureWithErrorThatMatches(containsString("staticMethod"));
+ } else {
+ compile.run(parameters.getRuntime(), ProgramValueOf.class).assertSuccessWithOutputLines("0");
+ compile
+ .run(parameters.getRuntime(), ProgramStaticMethod.class)
+ .assertSuccessWithOutputLines("42");
+ }
+ }
+
+ private Path compileLibrary() throws Exception {
+ return testForR8(Backend.CF)
+ .addProgramClasses(Lib.class, Lib.LibEnumStaticMethod.class, Lib.LibEnum.class)
+ .addKeepRules("-keep enum * { <fields>; }")
+ .addKeepRules(missingStaticMethods ? "" : "-keep enum * { static <methods>; }")
+ .addOptionsModification(
+ options -> {
+ options.enableEnumUnboxing = true;
+ options.testing.enableEnumUnboxingDebugLogs = true;
+ })
+ .addKeepClassRules(Lib.LibEnumStaticMethod.class)
+ .allowDiagnosticMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ msg -> {
+ assertEnumIsBoxed(
+ Lib.LibEnumStaticMethod.class,
+ Lib.LibEnumStaticMethod.class.getSimpleName(),
+ msg);
+ assertEnumIsBoxed(Lib.LibEnum.class, Lib.LibEnum.class.getSimpleName(), msg);
+ })
+ .writeToZip();
+ }
+
+ private void assertEnumUnboxedIfStaticMethodsPresent(TestDiagnosticMessages msg) {
+ if (missingStaticMethods) {
+ assertEnumIsBoxed(
+ Lib.LibEnumStaticMethod.class, Lib.LibEnumStaticMethod.class.getSimpleName(), msg);
+ assertEnumIsBoxed(Lib.LibEnum.class, Lib.LibEnum.class.getSimpleName(), msg);
+ } else {
+ assertEnumIsUnboxed(
+ Lib.LibEnumStaticMethod.class, Lib.LibEnumStaticMethod.class.getSimpleName(), msg);
+ assertEnumIsUnboxed(Lib.LibEnum.class, Lib.LibEnum.class.getSimpleName(), msg);
+ }
+ }
+
+ public static class Lib {
+
+ public enum LibEnumStaticMethod {
+ A,
+ B;
+
+ static int staticMethod() {
+ return 42;
+ }
+ }
+
+ public enum LibEnum {
+ A,
+ B
+ }
+ }
+
+ public static class ProgramValueOf {
+
+ public static void main(String[] args) {
+ System.out.println(Lib.LibEnumStaticMethod.valueOf(Lib.LibEnum.A.name()).ordinal());
+ }
+ }
+
+ public static class ProgramStaticMethod {
+
+ public static void main(String[] args) {
+ System.out.println(Lib.LibEnumStaticMethod.staticMethod());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
index 6fb5fd1..4f724d3 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
@@ -24,7 +24,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameterized.Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -32,7 +32,7 @@
}
public EnumUnboxingClassStaticizerTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -44,7 +44,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(EnumUnboxingClassStaticizerTest.class)
.addKeepMainRule(TestClass.class)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.noMinification() // For assertions.
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingReturnNullTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingReturnNullTest.java
index 2a1be5f..a0173b5 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingReturnNullTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingReturnNullTest.java
@@ -23,7 +23,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameterized.Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -31,7 +31,7 @@
}
public EnumUnboxingReturnNullTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -43,7 +43,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(classToTest, ENUM_CLASS)
.addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingSideEffectClInitTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingSideEffectClInitTest.java
index 5f05a31..128466c 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingSideEffectClInitTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingSideEffectClInitTest.java
@@ -17,7 +17,7 @@
private static final Class<?> ENUM_CLASS = MyEnum.class;
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -25,7 +25,7 @@
}
public EnumUnboxingSideEffectClInitTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -37,7 +37,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(EnumUnboxingSideEffectClInitTest.class)
.addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
@@ -47,7 +47,7 @@
m -> {
// The snap keep rule forces to keep the static MainEnum#e field, so the enum
// cannot be unboxed anymore.
- if (enumKeepRules.toString().equals("snap")) {
+ if (enumKeepRules.isSnap()) {
assertEnumIsBoxed(ENUM_CLASS, classToTest.getSimpleName(), m);
} else {
assertEnumIsUnboxed(ENUM_CLASS, classToTest.getSimpleName(), m);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
index 11ab1ae..1f82de1 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
@@ -13,44 +13,41 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
import java.util.List;
public class EnumUnboxingTestBase extends TestBase {
+ // Default keep rule present in Android Studio.
private static final String KEEP_ENUM_STUDIO =
"-keepclassmembers enum * {\n"
+ " public static **[] values();\n"
+ " public static ** valueOf(java.lang.String);\n"
+ "}";
+ // Default keep rule present in Snap.
private static final String KEEP_ENUM_SNAP =
"-keepclassmembers enum * {\n"
+ "<fields>;\n"
+ " public static **[] values();\n"
+ " public static ** valueOf(java.lang.String);\n"
+ "}";
- private static final List<KeepRule> KEEP_ENUM =
- ImmutableList.of(
- new KeepRule("none", ""),
- new KeepRule("studio", KEEP_ENUM_STUDIO),
- new KeepRule("snap", KEEP_ENUM_SNAP));
- public static class KeepRule {
- private final String name;
- private final String keepRule;
+ public enum EnumKeepRules {
+ NONE(""),
+ STUDIO(KEEP_ENUM_STUDIO),
+ SNAP(KEEP_ENUM_SNAP);
- private KeepRule(String name, String keepRule) {
- this.name = name;
- this.keepRule = keepRule;
+ private final String keepRules;
+
+ public String getKeepRules() {
+ return keepRules;
}
- public String getKeepRule() {
- return keepRule;
+ public boolean isSnap() {
+ return this == SNAP;
}
- @Override
- public String toString() {
- return name;
+ EnumKeepRules(String keepRules) {
+ this.keepRules = keepRules;
}
}
@@ -96,10 +93,10 @@
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(),
BooleanUtils.values(),
- KEEP_ENUM);
+ getAllEnumKeepRules());
}
- static List<KeepRule> getAllEnumKeepRules() {
- return KEEP_ENUM;
+ static EnumKeepRules[] getAllEnumKeepRules() {
+ return EnumKeepRules.values();
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
index de86bd7..2888ee4 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
@@ -18,7 +18,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -26,7 +26,7 @@
}
public EqualsCompareToEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -40,7 +40,7 @@
.addInnerClasses(EqualsCompareToEnumUnboxingTest.class)
.addKeepMainRule(EnumEqualscompareTo.class)
.enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
index f828413..08ec7bd 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
@@ -27,7 +27,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -35,7 +35,7 @@
}
public FailingEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -51,7 +51,7 @@
R8TestCompileResult compile =
r8FullTestBuilder
.enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
index 411a747..e42ac65 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
@@ -35,7 +35,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -43,7 +43,7 @@
}
public FailingMethodEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -55,7 +55,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(FailingMethodEnumUnboxingTest.class)
.addKeepMainRules(FAILURES)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.enableInliningAnnotations()
@@ -71,7 +71,7 @@
assertEnumIsBoxed(
failure.getDeclaredClasses()[0], failure.getSimpleName(), m))
.run(parameters.getRuntime(), failure);
- if (failure == EnumSetTest.class && enumKeepRules.getKeepRule().isEmpty()) {
+ if (failure == EnumSetTest.class && enumKeepRules.getKeepRules().isEmpty()) {
// EnumSet and EnumMap cannot be used without the enumKeepRules.
run.assertFailure();
} else {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
index 8ab4bcf..4d222d5 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
@@ -25,7 +25,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -33,7 +33,7 @@
}
public FieldPutEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -45,7 +45,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(FieldPutEnumUnboxingTest.class)
.addKeepMainRules(INPUTS)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
index 5dcf75d..fba8006 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
@@ -33,7 +33,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -41,7 +41,7 @@
}
public InterfaceEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -58,7 +58,7 @@
.enableMergeAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/JavaCGeneratedMethodTest.java b/src/test/java/com/android/tools/r8/enumunboxing/JavaCGeneratedMethodTest.java
index 728a346..5d4501f 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/JavaCGeneratedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/JavaCGeneratedMethodTest.java
@@ -19,7 +19,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameterized.Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -27,7 +27,7 @@
}
public JavaCGeneratedMethodTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -40,7 +40,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(classToTest, ENUM_CLASS)
.addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/NullOutValueInvokeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/NullOutValueInvokeEnumUnboxingTest.java
index c65427a..8e15ad1 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/NullOutValueInvokeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/NullOutValueInvokeEnumUnboxingTest.java
@@ -21,7 +21,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -29,7 +29,7 @@
}
public NullOutValueInvokeEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -42,7 +42,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(classToTest, ENUM_CLASS)
.addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
index 8bd41c3..79f9176 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
@@ -20,7 +20,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -28,7 +28,7 @@
}
public OrdinalHashCodeEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -41,7 +41,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(classToTest, ENUM_CLASS)
.addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingTest.java
index ba217f6..8aa2d5e 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingTest.java
@@ -21,7 +21,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -29,7 +29,7 @@
}
public PhiEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -42,7 +42,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(classToTest, ENUM_CLASS)
.addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/PinnedEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/PinnedEnumUnboxingTest.java
index fff500b..1c34891 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/PinnedEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/PinnedEnumUnboxingTest.java
@@ -20,7 +20,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -28,7 +28,7 @@
}
public PinnedEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -42,7 +42,7 @@
.addKeepMainRules(BOXED)
.addKeepClassRules(MainWithKeptEnum.MyEnum.class)
.addKeepMethodRules(MainWithKeptEnumArray.class, "keptMethod()")
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/StaticMethodsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/StaticMethodsEnumUnboxingTest.java
index 0d01ffe..390fe18 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/StaticMethodsEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/StaticMethodsEnumUnboxingTest.java
@@ -19,7 +19,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -27,7 +27,7 @@
}
public StaticMethodsEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -40,7 +40,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(StaticMethodsEnumUnboxingTest.class)
.addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
index dc2e217..76c06cb 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
@@ -21,7 +21,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -29,7 +29,7 @@
}
public SwitchEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -42,7 +42,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(SwitchEnumUnboxingTest.class)
.addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java
index b4f97ab..50a4d58 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java
@@ -18,7 +18,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -26,7 +26,7 @@
}
public ToStringEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -40,7 +40,7 @@
.addInnerClasses(ToStringEnumUnboxingTest.class)
.addKeepMainRule(EnumNameToString.class)
.enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java
index b7643a0..8b9b546 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java
@@ -18,7 +18,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -26,7 +26,7 @@
}
public ToStringOverrideEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -40,7 +40,7 @@
.addInnerClasses(ToStringOverrideEnumUnboxingTest.class)
.addKeepMainRule(EnumNameToString.class)
.enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java
index e2dcc4c..1da8c62 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingFailureTest.java
@@ -16,7 +16,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameterized.Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -24,7 +24,7 @@
}
public ValueOfEnumUnboxingFailureTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -37,7 +37,7 @@
.addInnerClasses(ValueOfEnumUnboxingFailureTest.class)
.addKeepMainRule(success)
.enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
index 37f31c9..23293f5 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
@@ -23,7 +23,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -31,7 +31,7 @@
}
public ValueOfEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -44,7 +44,7 @@
.addInnerClasses(ValueOfEnumUnboxingTest.class)
.addKeepMainRules(SUCCESSES)
.enableNeverClassInliningAnnotations()
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java
index 6496c7a..6323e8b 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java
@@ -19,7 +19,7 @@
private final TestParameters parameters;
private final boolean enumValueOptimization;
- private final KeepRule enumKeepRules;
+ private final EnumKeepRules enumKeepRules;
@Parameters(name = "{0} valueOpt: {1} keep: {2}")
public static List<Object[]> data() {
@@ -27,7 +27,7 @@
}
public VirtualMethodsEnumUnboxingTest(
- TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
@@ -40,7 +40,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(VirtualMethodsEnumUnboxingTest.class)
.addKeepMainRule(classToTest)
- .addKeepRules(enumKeepRules.getKeepRule())
+ .addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
index 5e7a17d..30f5c93 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
@@ -9,11 +9,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -28,20 +28,25 @@
public class UnusedArgumentsCollisionMappingTest extends TestBase {
private final TestParameters parameters;
+ private final CompilationMode compilationMode;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{0}, compilation mode: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), CompilationMode.values());
}
- public UnusedArgumentsCollisionMappingTest(TestParameters parameters) {
+ public UnusedArgumentsCollisionMappingTest(
+ TestParameters parameters, CompilationMode compilationMode) {
this.parameters = parameters;
+ this.compilationMode = compilationMode;
}
@Test
public void testR8() throws Exception {
R8TestRunResult runResult =
testForR8(parameters.getBackend())
+ .setMode(compilationMode)
.addProgramClasses(Main.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
@@ -50,14 +55,13 @@
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("test with unused", "test with used: foo")
.inspect(this::verifyArgumentsRemoved);
- // TODO(b/140851070): Duplicate entries for different methods.
assertEquals(
- 2,
+ 1,
StringUtils.splitLines(runResult.proguardMap()).stream()
.filter(line -> line.contains("void test(java.lang.String,java.lang.String)"))
.count());
assertEquals(
- 0,
+ 1,
StringUtils.splitLines(runResult.proguardMap()).stream()
.filter(line -> line.contains("void test(java.lang.String)"))
.count());
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderArgumentsTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderArgumentsTest.java
new file mode 100644
index 0000000..8b68bd2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderArgumentsTest.java
@@ -0,0 +1,226 @@
+// 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.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticPosition;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static com.android.tools.r8.PositionMatcher.positionLine;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ProguardMapReaderArgumentsTest {
+
+ private Reporter reporter;
+ private TestDiagnosticMessagesImpl testDiagnosticMessages;
+
+ @Before
+ public void setUp() {
+ testDiagnosticMessages = new TestDiagnosticMessagesImpl();
+ reporter = new Reporter(testDiagnosticMessages);
+ }
+
+ @Test
+ public void testMethodCanParseMemberComments() throws IOException {
+ String mapWithArgumentRemovalInformation =
+ StringUtils.join(
+ "\n",
+ "android.constraint.Placeholder -> a.b.b.f:",
+ "# Just a comment", // Regular comment
+ " int mContentId -> b",
+ "# {}", // Valid JSON with no information
+ " 147:161:void updatePreLayout(android.constraint.Layout) -> a",
+ "# {foo:bar}}}", // Not valid JSON
+ " 194:204:void updatePostMeasure(android.constraint.Layout) -> b",
+ "# { 'id': 'bar' }", // Valid json but no handler for bar.
+ " 194:204:void updatePostMeasure(android.constraint.Layout) -> c");
+ ClassNameMapper cnm =
+ ClassNameMapper.mapperFromString(mapWithArgumentRemovalInformation, reporter);
+ ClassNamingForNameMapper classNaming = cnm.getClassNaming("a.b.b.f");
+ assertNotNull(classNaming);
+ testDiagnosticMessages.assertOnlyInfos();
+ testDiagnosticMessages
+ .assertInfosMatch(
+ ImmutableList.of(
+ allOf(
+ diagnosticMessage(containsString("Could not locate 'id'")),
+ diagnosticPosition(positionLine(4))),
+ allOf(
+ diagnosticMessage(containsString("Not valid JSON")),
+ diagnosticPosition(positionLine(6))),
+ allOf(
+ diagnosticMessage(containsString("Could not find a handler for bar")),
+ diagnosticPosition(positionLine(8)))))
+ .assertAllInfosMatch(diagnosticType(MappingInformationDiagnostics.class));
+ }
+
+ @Test
+ public void testMethodWillReportWhenParsingArgumentsChangedMemberComments() throws IOException {
+ String mapWithArgumentRemovalInformation =
+ StringUtils.join(
+ "\n",
+ "android.constraint.Placeholder -> a.b.b.f:",
+ "# Just a comment", // Regular comment
+ " int mContentId -> b",
+ // Valid JSON but missing signature
+ "# { 'id': 'methodSignatureChanged', "
+ + "'signature': ['void', 'updatePreLayout', 'android.constraint.Layout'],"
+ + "'returnType': [], 'receiver': true, 'params': []}",
+ " 147:161:void updatePreLayout(android.constraint.Layout) -> a",
+ // Valid JSON but not an argumentsChanged object
+ "# { 'id': 'methodSignatureChanged', "
+ + "'signature': ['void', 'updatePreLayout', 'android.constraint.Layout'],"
+ + "'returnType': [], 'receiver': true, 'params': []}",
+ " 147:161:void updatePreLayout(android.constraint.Layout) -> a",
+ // Valid JSON but not an arguments_changed object.
+ "# { 'id': 'methodSignatureChanged', "
+ + "'signature': ['void','updatePreLayout','android.constraint.Layout'],"
+ + "'returnType': 'foo', 'receiver': 1, 'params': 'foo' }",
+ " 194:204:void updatePostMeasure(android.constraint.Layout) -> a")
+ .replace("'", "\"");
+ ClassNameMapper cnm =
+ ClassNameMapper.mapperFromString(mapWithArgumentRemovalInformation, reporter);
+ ClassNamingForNameMapper classNaming = cnm.getClassNaming("a.b.b.f");
+ assertNotNull(classNaming);
+ testDiagnosticMessages.assertOnlyInfos();
+ testDiagnosticMessages
+ .assertInfosMatch(
+ ImmutableList.of(
+ allOf(
+ diagnosticMessage(containsString("Could not decode")),
+ diagnosticPosition(positionLine(4))),
+ allOf(
+ diagnosticMessage(containsString("Could not decode")),
+ diagnosticPosition(positionLine(6))),
+ allOf(
+ diagnosticMessage(containsString("Could not decode")),
+ diagnosticPosition(positionLine(8)))))
+ .assertAllInfosMatch(diagnosticType(MappingInformationDiagnostics.class));
+ }
+
+ @Test
+ public void testMethodCanParseArgumentRemoval() throws IOException {
+ String mapWithArgumentRemovalInformation =
+ StringUtils.lines(
+ "android.constraint.Placeholder -> a.b.b.f:",
+ " int mContentId -> b",
+ "# { 'id': 'methodSignatureChanged',"
+ + "'signature': ["
+ + "'void','updatePreLayout','android.constraint.Layout','String','int'],"
+ + " 'returnType': 'void', 'receiver': true, 'params':[[1],[2]]}",
+ " 147:161:void updatePreLayout(android.constraint.Layout,String,int) -> a",
+ "# {'id':'methodSignatureChanged',"
+ + "'signature': ["
+ + "'void','updatePreMeasure','android.constraint.Layout','String','int'],"
+ + "'returnType': 'void', 'receiver': true, 'params':[[3]]}",
+ " 162:173:void updatePreMeasure(android.constraint.Layout,String,int) -> a",
+ " 194:204:void updatePostMeasure(android.constraint.Layout,String,int) -> a");
+ ClassNameMapper cnm = ClassNameMapper.mapperFromString(mapWithArgumentRemovalInformation);
+ ClassNamingForNameMapper classNaming = cnm.getClassNaming("a.b.b.f");
+ assertNotNull(classNaming);
+
+ List<MemberNaming> members = classNaming.lookupByOriginalName("mContentId");
+ assertFalse(members.isEmpty());
+ MemberNaming fieldContentId = members.get(0);
+ assertNotNull(fieldContentId);
+ assertTrue(!fieldContentId.isMethodNaming());
+
+ members = classNaming.lookupByOriginalName("updatePreLayout");
+ assertFalse(members.isEmpty());
+ MemberNaming updatePreLayout = members.get(0);
+ assertNotNull(updatePreLayout);
+ assertTrue(updatePreLayout.isMethodNaming());
+ MethodSignature renamedPreLayout = (MethodSignature) updatePreLayout.getRenamedSignature();
+ assertEquals(1, renamedPreLayout.parameters.length);
+ assertEquals("int", renamedPreLayout.parameters[0]);
+
+ members = classNaming.lookupByOriginalName("updatePreMeasure");
+ assertFalse(members.isEmpty());
+ MemberNaming updatePreMeasure = members.get(0);
+ assertNotNull(updatePreMeasure);
+ assertTrue(updatePreMeasure.isMethodNaming());
+ MethodSignature renamedPreMeasure = (MethodSignature) updatePreMeasure.getRenamedSignature();
+ assertEquals(2, renamedPreMeasure.parameters.length);
+ assertEquals("android.constraint.Layout", renamedPreMeasure.parameters[0]);
+ assertEquals("String", renamedPreMeasure.parameters[1]);
+
+ members = classNaming.lookupByOriginalName("updatePostMeasure");
+ assertFalse(members.isEmpty());
+ MemberNaming updatePostMeasure = members.get(0);
+ assertNotNull(updatePostMeasure);
+ assertTrue(updatePostMeasure.isMethodNaming());
+ MethodSignature renamedPostMeasure = (MethodSignature) updatePostMeasure.getRenamedSignature();
+ assertEquals(3, renamedPostMeasure.parameters.length);
+ }
+
+ @Test
+ public void testMethodCanParseArgumentChanged() throws IOException {
+ String mapWithArgumentRemovalInformation =
+ StringUtils.join(
+ "\n",
+ "android.constraint.Placeholder -> a.b.b.f:",
+ "# {'id':'methodSignatureChanged',"
+ + "'signature':["
+ + "'void','updatePreLayout','android.constraint.Layout','String','float'],"
+ + "'returnType': 'void',"
+ + "'receiver': true,"
+ + "'params':[[1,int],[2,Foo]]}",
+ "# {'id':'methodSignatureChanged',"
+ + "'signature':["
+ + "'void','updatePreMeasure','android.constraint.Layout','String','int'],"
+ + "'returnType': 'void', "
+ + "'receiver': true, "
+ + "'params':[[2,com.baz.Bar],[3]]}",
+ " 147:161:void updatePreLayout(android.constraint.Layout,String,float) -> a",
+ " 162:173:void updatePreMeasure(android.constraint.Layout,String,int) -> a",
+ " 194:204:void updatePostMeasure(android.constraint.Layout,String,int) -> a");
+ ClassNameMapper cnm = ClassNameMapper.mapperFromString(mapWithArgumentRemovalInformation);
+ ClassNamingForNameMapper classNaming = cnm.getClassNaming("a.b.b.f");
+ assertNotNull(classNaming);
+
+ List<MemberNaming> members = classNaming.lookupByOriginalName("updatePreLayout");
+ assertFalse(members.isEmpty());
+ MemberNaming updatePreLayout = members.get(0);
+ assertNotNull(updatePreLayout);
+ assertTrue(updatePreLayout.isMethodNaming());
+ MethodSignature renamedPreLayout = (MethodSignature) updatePreLayout.getRenamedSignature();
+ assertEquals(3, renamedPreLayout.parameters.length);
+ assertEquals("int", renamedPreLayout.parameters[0]);
+ assertEquals("Foo", renamedPreLayout.parameters[1]);
+ assertEquals("float", renamedPreLayout.parameters[2]);
+
+ members = classNaming.lookupByOriginalName("updatePreMeasure");
+ assertFalse(members.isEmpty());
+ MemberNaming updatePreMeasure = members.get(0);
+ assertNotNull(updatePreMeasure);
+ assertTrue(updatePreMeasure.isMethodNaming());
+ MethodSignature renamedPreMeasure = (MethodSignature) updatePreMeasure.getRenamedSignature();
+ assertEquals(2, renamedPreMeasure.parameters.length);
+ assertEquals("android.constraint.Layout", renamedPreMeasure.parameters[0]);
+ assertEquals("com.baz.Bar", renamedPreMeasure.parameters[1]);
+
+ members = classNaming.lookupByOriginalName("updatePostMeasure");
+ assertFalse(members.isEmpty());
+ MemberNaming updatePostMeasure = members.get(0);
+ assertNotNull(updatePostMeasure);
+ assertTrue(updatePostMeasure.isMethodNaming());
+ MethodSignature renamedPostMeasure = (MethodSignature) updatePostMeasure.getRenamedSignature();
+ assertEquals(3, renamedPostMeasure.parameters.length);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 2d5e31b..d2b2aba 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -280,6 +280,10 @@
}
public StackTrace retrace(String map) {
+ return retrace(map, null);
+ }
+
+ public StackTrace retrace(String map, String regularExpression) {
class Box {
List<String> result;
}
@@ -291,6 +295,7 @@
stackTraceLines.stream()
.map(line -> line.originalLine)
.collect(Collectors.toList()))
+ .setRegularExpression(regularExpression)
.setRetracedStackTraceConsumer(retraced -> box.result = retraced)
.build());
// Keep the original stderr in the retraced stacktrace.
diff --git a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
new file mode 100644
index 0000000..0c3206c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
@@ -0,0 +1,176 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.regress;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress160394262Test extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("1;2;3");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public Regress160394262Test(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(Regress160394262Test.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .allowAccessModification()
+ .addKeepMainRule(TestClass.class)
+ .addInnerClasses(Regress160394262Test.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkJoinerIsClassInlined);
+ }
+
+ private void checkJoinerIsClassInlined(CodeInspector inspector) {
+ // TODO(b/160640028): When compiling to DEX a trivial phi remains in the inline code preventing
+ // class inlining of Joiner and the anonymous Joiner subclass.
+ if (parameters.isDexRuntime()) {
+ assertThat(inspector.clazz(Joiner.class), isPresent());
+ assertThat(inspector.clazz(Joiner.class.getTypeName() + "$1"), isPresent());
+ return;
+ }
+ assertThat(inspector.clazz(Joiner.class), not(isPresent()));
+ assertThat(inspector.clazz(Joiner.class.getTypeName() + "$1"), not(isPresent()));
+ }
+
+ static class TestClass {
+
+ public void foo(List<?> items) {
+ System.out.println(Joiner.on(";").skipNulls().join(items));
+ }
+
+ public static void main(String[] args) {
+ new TestClass().foo(Arrays.asList("1", 2, 3l, null));
+ }
+ }
+
+ // Minimized copy of com.google.common.base.Preconditions
+ static class Preconditions {
+
+ public static <T> T checkNotNull(T reference) {
+ if (reference == null) {
+ throw new NullPointerException();
+ } else {
+ return reference;
+ }
+ }
+
+ public static <T> T checkNotNull(T reference, Object errorMessage) {
+ if (reference == null) {
+ throw new NullPointerException(String.valueOf(errorMessage));
+ } else {
+ return reference;
+ }
+ }
+ }
+
+ // Minimized copy of com.google.common.base.Joiner
+ static class Joiner {
+
+ private final String separator;
+
+ public static Joiner on(String separator) {
+ return new Joiner(separator);
+ }
+
+ private Joiner(String separator) {
+ this.separator = Preconditions.checkNotNull(separator);
+ }
+
+ private Joiner(Joiner prototype) {
+ this.separator = prototype.separator;
+ }
+
+ public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
+ Preconditions.checkNotNull(appendable);
+ if (parts.hasNext()) {
+ appendable.append(this.toString(parts.next()));
+ while (parts.hasNext()) {
+ appendable.append(this.separator);
+ appendable.append(this.toString(parts.next()));
+ }
+ }
+ return appendable;
+ }
+
+ public final String join(Iterable<?> parts) {
+ return join(parts.iterator());
+ }
+
+ public final String join(Iterator<?> parts) {
+ StringBuilder builder = new StringBuilder();
+ try {
+ appendTo((Appendable) builder, parts);
+ return builder.toString();
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public Joiner skipNulls() {
+ return new Joiner(this) {
+ public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts)
+ throws IOException {
+ Preconditions.checkNotNull(appendable, "appendable");
+ Preconditions.checkNotNull(parts, "parts");
+ Object part;
+ while (parts.hasNext()) {
+ part = parts.next();
+ if (part != null) {
+ appendable.append(Joiner.this.toString(part));
+ break;
+ }
+ }
+ while (parts.hasNext()) {
+ part = parts.next();
+ if (part != null) {
+ appendable.append(Joiner.this.separator);
+ appendable.append(Joiner.this.toString(part));
+ }
+ }
+ return appendable;
+ }
+ };
+ }
+
+ CharSequence toString(Object part) {
+ Preconditions.checkNotNull(part);
+ return part instanceof CharSequence ? (CharSequence) part : part.toString();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java b/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java
new file mode 100644
index 0000000..bbe2d5b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.DiagnosticsMatcher;
+import com.android.tools.r8.PositionMatcher;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DuplicateMappingsTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public DuplicateMappingsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testSourceFileName() {
+ TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+ Retrace.run(
+ RetraceCommand.builder(diagnosticsHandler)
+ .setProguardMapProducer(
+ () ->
+ StringUtils.lines(
+ "com.android.tools.r8.retrace.SourceFileTest$ClassWithCustomFileName ->"
+ + " com.android.tools.r8.retrace.a:",
+ "# {'id':'sourceFile','fileName':'foobarbaz.java'}",
+ "# {'id':'sourceFile','fileName':'foobarbaz2.java'}"))
+ .setStackTrace(ImmutableList.of())
+ .setRetracedStackTraceConsumer(
+ strings -> {
+ // No need to do anything, we are just checking for diagnostics.
+ })
+ .build());
+ diagnosticsHandler
+ .assertWarningsCount(1)
+ .assertWarningsMatch(
+ allOf(
+ DiagnosticsMatcher.diagnosticMessage(containsString("The mapping")),
+ DiagnosticsMatcher.diagnosticMessage(
+ containsString("is not allowed in combination with")),
+ DiagnosticsMatcher.diagnosticPosition(PositionMatcher.positionLine(3))));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java b/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java
new file mode 100644
index 0000000..fb7f280
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java
@@ -0,0 +1,140 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.List;
+import java.util.function.BiConsumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SourceFileTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean useRegularExpression;
+ private static final String FILE_NAME = "foobarbaz.java";
+
+ @Parameters(name = "{0}, useRegularExpression: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ public SourceFileTest(TestParameters parameters, boolean useRegularExpression) {
+ this.parameters = parameters;
+ this.useRegularExpression = useRegularExpression;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, ClassWithoutCustomFileName.class)
+ .addProgramClassFileData(
+ transformer(ClassWithCustomFileName.class).setSourceFile(FILE_NAME).transform())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(containsString("Hello World!"))
+ .inspectStackTrace(
+ stackTrace -> {
+ assertEquals(FILE_NAME, stackTrace.getStackTraceLines().get(0).fileName);
+ });
+ }
+
+ @Test
+ public void testR8WithCustomFileName() throws Exception {
+ runTest(
+ false,
+ ((stackTrace, inspector) -> {
+ assertEquals(FILE_NAME, stackTrace.getStackTraceLines().get(0).fileName);
+ assertEquals(
+ 1,
+ inspector
+ .clazz(ClassWithCustomFileName.class)
+ .getNaming()
+ .getAdditionalMappings()
+ .size());
+ }));
+ }
+
+ @Test
+ public void testR8WithoutCustomFileName() throws Exception {
+ runTest(
+ true,
+ ((stackTrace, inspector) -> {
+ assertEquals("SourceFileTest.java", stackTrace.getStackTraceLines().get(0).fileName);
+ assertEquals(
+ 0,
+ inspector
+ .clazz(ClassWithoutCustomFileName.class)
+ .getNaming()
+ .getAdditionalMappings()
+ .size());
+ }));
+ }
+
+ private void runTest(boolean addDummyArg, BiConsumer<StackTrace, CodeInspector> consumer)
+ throws Exception {
+ R8FullTestBuilder r8FullTestBuilder =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, ClassWithoutCustomFileName.class)
+ .addProgramClassFileData(
+ transformer(ClassWithCustomFileName.class).setSourceFile(FILE_NAME).transform())
+ .addKeepClassRules(ClassWithoutCustomFileName.class)
+ .enableInliningAnnotations()
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributeSourceFile();
+ R8TestRunResult runResult =
+ addDummyArg
+ ? r8FullTestBuilder.run(parameters.getRuntime(), Main.class, "foo")
+ : r8FullTestBuilder.run(parameters.getRuntime(), Main.class);
+ runResult.assertFailureWithErrorThatMatches(containsString("Hello World!"));
+ StackTrace originalStackTrace = runResult.getOriginalStackTrace();
+ StackTrace retracedStackTrace =
+ originalStackTrace.retrace(
+ runResult.proguardMap(),
+ useRegularExpression ? Retrace.DEFAULT_REGULAR_EXPRESSION : null);
+ runResult.inspectFailure(inspector -> consumer.accept(retracedStackTrace, inspector));
+ }
+
+ public static class ClassWithoutCustomFileName {
+
+ @NeverInline
+ public static void foo() {
+ throw new RuntimeException("Hello World!");
+ }
+ }
+
+ public static class ClassWithCustomFileName {
+
+ @NeverInline
+ public static void foo() {
+ throw new RuntimeException("Hello World!");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ ClassWithCustomFileName.foo();
+ } else {
+ ClassWithoutCustomFileName.foo();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index ae4947a..1bf5f83 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.util.List;
import java.util.Objects;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -74,13 +75,14 @@
if (enableOptimization) {
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("simple"), 1);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("local"), 1);
+ // TODO(b/160667929): Re-enable name()/toString() optimizations.
// String concatenation optimization is enabled for DEX output.
// Even replaced ordinal is concatenated (and gone).
- if (parameters.isDexRuntime()) {
- assertOrdinalReplacedAndGone(clazz.uniqueMethodWithName("multipleUsages"));
- } else {
- assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("multipleUsages"), 1);
- }
+ // if (parameters.isDexRuntime()) {
+ // assertOrdinalReplacedAndGone(clazz.uniqueMethodWithName("multipleUsages"));
+ // } else {
+ assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("multipleUsages"), 1);
+ // }
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inlined"), 1);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inSwitch"), 11);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), 1);
@@ -121,6 +123,7 @@
assertTrue(clazz.isPresent());
if (enableOptimization) {
+ Assume.assumeTrue("TODO(b/160667929): Re-enable name()/toString() optimizations.", false);
assertNameReplacedWithConst(clazz.uniqueMethodWithName("simple"), "TWO");
assertNameReplacedWithConst(clazz.uniqueMethodWithName("local"), "TWO");
// String concatenation optimization is enabled for DEX output.
@@ -171,6 +174,7 @@
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("valueWithoutToString"));
if (enableOptimization) {
+ Assume.assumeTrue("TODO(b/160667929): Re-enable name()/toString() optimizations.", false);
assertToStringReplacedWithConst(clazz.uniqueMethodWithName("noToString"), "TWO");
assertToStringReplacedWithConst(clazz.uniqueMethodWithName("local"), "TWO");
assertToStringReplacedWithConst(clazz.uniqueMethodWithName("multipleUsages"), "TWO");
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 1f1d744..c34dc98 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -266,6 +266,16 @@
});
}
+ public ClassFileTransformer setSourceFile(String sourceFile) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public void visitSource(String source, String debug) {
+ super.visitSource(sourceFile, debug);
+ }
+ });
+ }
+
public ClassFileTransformer setAccessFlags(Consumer<ClassAccessFlags> fn) {
return addClassTransformer(
new ClassTransformer() {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 6a04a0a..59c8760 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.references.ClassReference;
import java.util.List;
import java.util.function.Consumer;
@@ -177,4 +178,9 @@
public KotlinClassMetadata getKotlinClassMetadata() {
return null;
}
+
+ @Override
+ public ClassNamingForNameMapper getNaming() {
+ return null;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 84d76a1..b2266ce 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
@@ -211,4 +212,6 @@
Reference.classFromDescriptor(
descriptor.substring(0, descriptor.length() - 1) + COMPANION_CLASS_NAME_SUFFIX + ";"));
}
+
+ public abstract ClassNamingForNameMapper getNaming();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index bd65812..0b1d950 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -403,4 +403,9 @@
return KotlinClassMetadataReader.toKotlinClassMetadata(
codeInspector.getFactory().kotlin, annotationSubject.getAnnotation());
}
+
+ @Override
+ public ClassNamingForNameMapper getNaming() {
+ return naming;
+ }
}
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 511ec8d..962bc23 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -3,15 +3,15 @@
# 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.
+import archive
import argparse
+import jdk
import os
+import retrace
import subprocess
import sys
import zipfile
-import archive
-import jdk
-import retrace
import utils
@@ -65,6 +65,18 @@
help='Enable Java debug agent and suspend compilation (default disabled)',
default=False,
action='store_true')
+ parser.add_argument(
+ '--xmx',
+ help='Set JVM max heap size (-Xmx)',
+ default=None)
+ parser.add_argument(
+ '--threads',
+ help='Set the number of threads to use',
+ default=None)
+ parser.add_argument(
+ '--min-api',
+ help='Set min-api (default read from dump properties file)',
+ default=None)
return parser
def error(msg):
@@ -155,6 +167,13 @@
def determine_output(args, temp):
return os.path.join(temp, 'out.jar')
+def determine_min_api(args, build_properties):
+ if args.min_api:
+ return args.min_api
+ if 'min-api' in build_properties:
+ return build_properties.get('min-api')
+ return None
+
def determine_feature_output(feature_jar, temp):
return os.path.join(temp, os.path.basename(feature_jar)[:-4] + ".out.jar")
@@ -193,6 +212,7 @@
version = determine_version(args, dump)
compiler = determine_compiler(args, dump)
out = determine_output(args, temp)
+ min_api = determine_min_api(args, build_properties)
jar = args.r8_jar if args.r8_jar else download_distribution(args, version, temp)
wrapper_dir = prepare_wrapper(jar, temp)
cmd = [jdk.GetJavaExecutable()]
@@ -201,6 +221,8 @@
print "WARNING: Running debugging agent on r8lib is questionable..."
cmd.append(
'-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005')
+ if args.xmx:
+ cmd.append('-Xmx' + args.xmx)
if args.ea:
cmd.append('-ea')
if args.printtimes:
@@ -225,8 +247,10 @@
cmd.extend(['--pg-conf', dump.config_file()])
if compiler != 'd8':
cmd.extend(['--pg-map-output', '%s.map' % out])
- if 'min-api' in build_properties:
- cmd.extend(['--min-api', build_properties.get('min-api')])
+ if min_api:
+ cmd.extend(['--min-api', min_api])
+ if args.threads:
+ cmd.extend(['--threads', args.threads])
cmd.extend(otherargs)
utils.PrintCmd(cmd)
try: