Merge "Add tests for default ctor of Serializable and Externalizable."
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 6fe8ad0..d28e045 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
@@ -42,10 +43,11 @@
try {
DexApplication application =
new ApplicationReader(app, options, timing).read(executor).toDirect();
- AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+ AppView<? extends AppInfoWithSubtyping> appView =
+ new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
RootSet mainDexRootSet =
- new RootSetBuilder(appInfo, application, options.mainDexKeepRules, options).run(executor);
- Enqueuer enqueuer = new Enqueuer(appInfo, GraphLense.getIdentityLense(), options, true);
+ new RootSetBuilder(appView, application, options.mainDexKeepRules, options).run(executor);
+ Enqueuer enqueuer = new Enqueuer(appView, options, true);
AppInfoWithLiveness mainDexAppInfo = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
// LiveTypes is the result.
Set<DexType> mainDexClasses =
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 78cb75a..4c52b53 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -5,9 +5,11 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RootSetBuilder;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -76,21 +78,21 @@
private static void run(
R8Command command, Set<String> descriptors, InternalOptions options, ExecutorService executor)
throws IOException {
+ assert !options.forceProguardCompatibility;
Timing timing = new Timing("PrintSeeds");
try {
DexApplication application =
new ApplicationReader(command.getInputApp(), options, timing).read(executor).toDirect();
- AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+ AppView<? extends AppInfoWithSubtyping> appView =
+ new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
RootSet rootSet =
new RootSetBuilder(
- appInfo, application, options.proguardConfiguration.getRules(), options)
+ appView, application, options.proguardConfiguration.getRules(), options)
.run(executor);
- Enqueuer enqueuer = new Enqueuer(appInfo, GraphLense.getIdentityLense(), options, false);
- appInfo = enqueuer.traceApplication(rootSet, executor, timing);
+ Enqueuer enqueuer = new Enqueuer(appView, options);
+ AppInfoWithLiveness appInfo = enqueuer.traceApplication(rootSet, executor, timing);
RootSetBuilder.writeSeeds(
- appInfo.withLiveness(),
- System.out,
- type -> descriptors.contains(type.toDescriptorString()));
+ appInfo, System.out, type -> descriptors.contains(type.toDescriptorString()));
} catch (ExecutionException e) {
throw R8.unwrapExecutionException(e);
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8be6306..ec863ee 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -275,24 +275,15 @@
// kotlin metadata annotation is removed.
computeKotlinInfoForProgramClasses(application, appView.appInfo());
- final ProguardConfiguration.Builder compatibility =
+ ProguardConfiguration.Builder compatibility =
ProguardConfiguration.builder(application.dexItemFactory, options.reporter);
rootSet =
new RootSetBuilder(
- appView.appInfo(),
- application,
- options.proguardConfiguration.getRules(),
- options)
+ appView, application, options.proguardConfiguration.getRules(), options)
.run(executorService);
- Enqueuer enqueuer =
- new Enqueuer(
- appView.appInfo(),
- appView.graphLense(),
- options,
- options.forceProguardCompatibility,
- compatibility);
+ Enqueuer enqueuer = new Enqueuer(appView, options, compatibility);
appView.setAppInfo(enqueuer.traceApplication(rootSet, executorService, timing));
if (options.proguardConfiguration.isPrintSeeds()) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
@@ -416,10 +407,10 @@
if (!options.mainDexKeepRules.isEmpty()) {
appView.setAppInfo(new AppInfoWithSubtyping(application));
- Enqueuer enqueuer = new Enqueuer(appView.appInfo(), appView.graphLense(), options, true);
+ Enqueuer enqueuer = new Enqueuer(appView, options, true);
// Lets find classes which may have code executed before secondary dex files installation.
RootSet mainDexRootSet =
- new RootSetBuilder(appView.appInfo(), application, options.mainDexKeepRules, options)
+ new RootSetBuilder(appView, application, options.mainDexKeepRules, options)
.run(executorService);
AppInfoWithLiveness mainDexAppInfo =
enqueuer.traceMainDex(mainDexRootSet, executorService, timing);
@@ -448,12 +439,7 @@
if (options.enableTreeShaking || options.enableMinification) {
timing.begin("Post optimization code stripping");
try {
- Enqueuer enqueuer =
- new Enqueuer(
- appView.appInfo(),
- appView.graphLense(),
- options,
- options.forceProguardCompatibility);
+ Enqueuer enqueuer = new Enqueuer(appView, options);
appView.setAppInfo(enqueuer.traceApplication(rootSet, executorService, timing));
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index aeaac60..3dbaa56 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.4.1-dev";
+ public static final String LABEL = "1.4.2-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
index 9416a7b..57151fa 100644
--- a/src/main/java/com/android/tools/r8/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -38,6 +38,11 @@
}
@Override
+ public boolean isCheckCast() {
+ return true;
+ }
+
+ @Override
public void registerUse(UseRegistry registry) {
registry.registerCheckCast(getType());
}
diff --git a/src/main/java/com/android/tools/r8/code/ConstString.java b/src/main/java/com/android/tools/r8/code/ConstString.java
index d165de5..ed7847a 100644
--- a/src/main/java/com/android/tools/r8/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/code/ConstString.java
@@ -45,6 +45,11 @@
}
@Override
+ public boolean isConstString() {
+ return true;
+ }
+
+ @Override
public String toString(ClassNameMapper naming) {
return formatString("v" + AA + ", \"" + BBBB.toString() + "\"");
}
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index c8b5154..fbb93ca 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -122,6 +122,14 @@
this.offset = offset;
}
+ public boolean isCheckCast() {
+ return false;
+ }
+
+ public boolean isConstString() {
+ return false;
+ }
+
public boolean isSimpleNop() {
return !isPayload() && this instanceof Nop;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
index 0c80f96..6b368b3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
@@ -31,7 +31,7 @@
}
@Override
- TypeLatticeElement asNullable() {
+ public TypeLatticeElement asNullable() {
return isNullable() ? this : new ArrayTypeLatticeElement(type, true);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java
index b9a45d2..dba0a2a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java
@@ -14,7 +14,7 @@
}
@Override
- TypeLatticeElement asNullable() {
+ public TypeLatticeElement asNullable() {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
index 72825a9..de75d5f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
@@ -27,7 +27,7 @@
}
@Override
- TypeLatticeElement asNullable() {
+ public TypeLatticeElement asNullable() {
return isNullable() ? this : new ClassTypeLatticeElement(type, true, interfaces);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/NullLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/NullLatticeElement.java
new file mode 100644
index 0000000..caad0a2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/NullLatticeElement.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.type;
+
+/**
+ * Encodes the following lattice.
+ *
+ * <pre>
+ * MAYBE NULL
+ * / \
+ * DEFINITELY DEFINITELY
+ * NULL NOT NULL
+ * \ /
+ * BOTTOM
+ * </pre>
+ */
+public class NullLatticeElement {
+
+ private static final NullLatticeElement BOTTOM = new NullLatticeElement();
+ private static final NullLatticeElement DEFINITELY_NULL = new NullLatticeElement();
+ private static final NullLatticeElement DEFINITELY_NOT_NULL = new NullLatticeElement();
+ private static final NullLatticeElement MAYBE_NULL = new NullLatticeElement();
+
+ private NullLatticeElement() {}
+
+ public boolean isDefinitelyNull() {
+ return this == DEFINITELY_NULL;
+ }
+
+ public boolean isDefinitelyNotNull() {
+ return this == DEFINITELY_NOT_NULL;
+ }
+
+ public NullLatticeElement leastUpperBound(NullLatticeElement other) {
+ if (this == BOTTOM) {
+ return other;
+ }
+ if (this == other || other == BOTTOM) {
+ return this;
+ }
+ return MAYBE_NULL;
+ }
+
+ public boolean lessThanOrEqual(NullLatticeElement other) {
+ return leastUpperBound(other) == other;
+ }
+
+ static NullLatticeElement bottom() {
+ return BOTTOM;
+ }
+
+ static NullLatticeElement definitelyNull() {
+ return DEFINITELY_NULL;
+ }
+
+ static NullLatticeElement definitelyNotNull() {
+ return DEFINITELY_NOT_NULL;
+ }
+
+ static NullLatticeElement maybeNull() {
+ return MAYBE_NULL;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
index 7cd6a6a..53c67e4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
@@ -18,7 +18,7 @@
}
@Override
- TypeLatticeElement asNullable() {
+ public TypeLatticeElement asNullable() {
return TypeLatticeElement.TOP;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
index 387d6c5..ef36c4e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
@@ -49,7 +49,7 @@
}
@Override
- TypeLatticeElement asNullable() {
+ public TypeLatticeElement asNullable() {
assert isNull() || isReferenceInstance();
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java
index b028963..7aba6dc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java
@@ -14,7 +14,7 @@
}
@Override
- TypeLatticeElement asNullable() {
+ public TypeLatticeElement asNullable() {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
index ed30610..a09039c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
@@ -37,6 +37,7 @@
public static final ReferenceTypeLatticeElement REFERENCE =
ReferenceTypeLatticeElement.getReferenceTypeLatticeElement();
+ // TODO(b/72693244): Switch to NullLatticeElement.
private final boolean isNullable;
TypeLatticeElement(boolean isNullable) {
@@ -47,12 +48,22 @@
return isNullable;
}
+ public NullLatticeElement nullElement() {
+ if (isNull()) {
+ return NullLatticeElement.definitelyNull();
+ }
+ if (!isNullable()) {
+ return NullLatticeElement.definitelyNotNull();
+ }
+ return NullLatticeElement.maybeNull();
+ }
+
/**
* Defines how to join with null or switch to nullable lattice element.
*
* @return {@link TypeLatticeElement} a result of joining with null.
*/
- abstract TypeLatticeElement asNullable();
+ public abstract TypeLatticeElement asNullable();
/**
* Defines how to switch to non-nullable lattice element.
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index ae508e8..a8c9b2f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
import com.android.tools.r8.graph.DexItemFactory;
@@ -69,6 +70,11 @@
return true;
}
+ public boolean verifyTypes(AppInfo appInfo) {
+ assert instructions.stream().allMatch(instruction -> instruction.verifyTypes(appInfo));
+ return true;
+ }
+
public void setLocalsAtEntry(Int2ReferenceMap<DebugLocalInfo> localsAtEntry) {
this.localsAtEntry = localsAtEntry;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 4c1824b..8d461bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -123,6 +123,47 @@
}
@Override
+ public boolean verifyTypes(AppInfo appInfo) {
+ TypeLatticeElement inType = object().getTypeLattice();
+
+ // TODO(b/72693244): There should never be a value with imprecise type lattice.
+ if (!inType.isPreciseType()) {
+ return true;
+ }
+
+ TypeLatticeElement outType = outValue().getTypeLattice();
+ TypeLatticeElement castType =
+ TypeLatticeElement.fromDexType(getType(), appInfo, inType.isNullable());
+
+ if (TypeLatticeElement.lessThanOrEqual(appInfo, inType, castType)) {
+ // Cast can be removed. Check that it is sound to replace all users of the out-value by the
+ // in-value.
+ assert TypeLatticeElement.lessThanOrEqual(appInfo, inType, outType);
+
+ // TODO(b/72693244): Consider checking equivalence. This requires that the types are always
+ // as precise as possible, though, meaning that almost all changes to the IR must be followed
+ // by a fix-point analysis.
+ // assert outType.equals(inType);
+ } else {
+ // We don't have enough information to remove the cast. Check that the out-value does not
+ // have a more precise type than the cast-type.
+ assert castType.asNullable().equals(outType.asNullable());
+
+ // Check soundness of null information.
+ assert inType.nullElement().lessThanOrEqual(outType.nullElement());
+
+ // Since we cannot remove the cast the in-value must be different from null.
+ assert !inType.isNull();
+
+ // TODO(b/72693244): Consider checking equivalence. This requires that the types are always
+ // as precise as possible, though, meaning that almost all changes to the IR must be followed
+ // by a fix-point analysis.
+ // assert outType.equals(castType);
+ }
+ return true;
+ }
+
+ @Override
public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
helper.loadInValues(this, it);
helper.storeOutValue(this, it);
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index b1e4e28..92e2ddb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -23,6 +24,7 @@
import java.util.Map;
import java.util.Queue;
import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
public class IRCode {
@@ -408,6 +410,7 @@
assert consistentDefUseChains();
assert validThrowingInstructions();
assert noCriticalEdges();
+ assert noBottomTypeLatticeLeft();
return true;
}
@@ -421,6 +424,11 @@
return true;
}
+ public boolean verifyTypes(AppInfo appInfo) {
+ assert blocks.stream().allMatch(block -> block.verifyTypes(appInfo));
+ return true;
+ }
+
private boolean noCriticalEdges() {
for (BasicBlock block : blocks) {
List<BasicBlock> predecessors = block.getPredecessors();
@@ -606,6 +614,29 @@
return true;
}
+ private boolean noBottomTypeLatticeLeft() {
+ return verifySSATypeLattice(lattice -> !lattice.isBottom());
+ }
+
+ private boolean noImpreciseTypeLatticeLeft() {
+ return verifySSATypeLattice(TypeLatticeElement::isPreciseType);
+ }
+
+ private boolean verifySSATypeLattice(Predicate<TypeLatticeElement> tester) {
+ for (BasicBlock block : blocks) {
+ for (Instruction instruction : block.getInstructions()) {
+ Value outValue = instruction.outValue();
+ if (outValue != null) {
+ assert tester.test(outValue.getTypeLattice());
+ }
+ }
+ for (Phi phi : block.getPhis()) {
+ assert tester.test(phi.getTypeLattice());
+ }
+ }
+ return true;
+ }
+
public InstructionIterator instructionIterator() {
return new IRCodeInstructionsIterator(this);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 8f1a004..d3355b4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -1089,6 +1089,11 @@
"Implement type lattice evaluation for: " + getInstructionName());
}
+ public boolean verifyTypes(AppInfo appInfo) {
+ // TODO(b/72693244): for instructions with invariant out type, we can verify type directly here.
+ return true;
+ }
+
/**
* Indicates whether the instruction throws a NullPointerException if the object denoted by the
* given value is null at runtime execution.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 7e5759a..3c657ec 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -790,6 +790,9 @@
if (devirtualizer != null) {
devirtualizer.devirtualizeInvokeInterface(code, method.method.getHolder());
}
+
+ assert code.verifyTypes(appInfo);
+
codeRewriter.removeCasts(code);
codeRewriter.rewriteLongCompareAndRequireNonNull(code, options);
codeRewriter.commonSubexpressionElimination(code);
@@ -834,6 +837,8 @@
assert code.isConsistentSSA();
}
+ assert code.verifyTypes(appInfo);
+
if (classInliner != null) {
// Class inliner should work before lambda merger, so if it inlines the
// lambda, it does not get collected by merger.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 02c06da..f8b85de 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1661,25 +1661,29 @@
TypeLatticeElement outTypeLattice = outValue.getTypeLattice();
TypeLatticeElement castTypeLattice =
TypeLatticeElement.fromDexType(castType, appInfo, inTypeLattice.isNullable());
- // 1) Trivial cast.
- // A a = ...
- // A a' = (A) a;
- // 2) Up-cast: we already have finer type info.
- // A < B
- // A a = ...
- // B b = (B) a;
+
+ assert inTypeLattice.nullElement().lessThanOrEqual(outTypeLattice.nullElement());
+
if (TypeLatticeElement.lessThanOrEqual(appInfo, inTypeLattice, castTypeLattice)) {
- assert outTypeLattice.equals(inTypeLattice);
+ // 1) Trivial cast.
+ // A a = ...
+ // A a' = (A) a;
+ // 2) Up-cast: we already have finer type info.
+ // A < B
+ // A a = ...
+ // B b = (B) a;
+ assert TypeLatticeElement.lessThanOrEqual(appInfo, inTypeLattice, outTypeLattice);
needToRemoveTrivialPhis = needToRemoveTrivialPhis || outValue.numberOfPhiUsers() != 0;
removeOrReplaceByDebugLocalWrite(checkCast, it, inValue, outValue);
- continue;
+ } else {
+ // Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
+ // A < B < C
+ // c = ... // Even though we know c is of type A,
+ // a' = (B) c; // (this could be removed, since chained below.)
+ // a'' = (A) a'; // this should remain for runtime verification.
+ assert !inTypeLattice.isNull();
+ assert outTypeLattice.asNullable().equals(castTypeLattice.asNullable());
}
- // Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
- // A < B < C
- // c = ... // Even though we know c is of type A,
- // a' = (B) c; // (this could be removed, since chained below.)
- // a'' = (A) a'; // this should remain for runtime verification.
- assert outTypeLattice.equals(castTypeLattice);
}
}
// ... v1
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 0ef4597..6999f9a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -137,6 +137,7 @@
// of roots to avoid infinite inlining. Looping makes possible for some roots to
// become eligible after other roots are inlined.
+ boolean anyInlinedMethods = false;
boolean repeat;
do {
repeat = false;
@@ -169,18 +170,24 @@
}
// Inline the class instance.
- boolean anyInlinedMethods = processor.processInlining(code, inliner);
+ anyInlinedMethods |= processor.processInlining(code, inliner);
// Restore normality.
code.removeAllTrivialPhis();
assert code.isConsistentSSA();
- if (anyInlinedMethods) {
- codeRewriter.simplifyIf(code);
- }
rootsIterator.remove();
repeat = true;
}
} while (repeat);
+
+ if (anyInlinedMethods) {
+ // If a method was inlined we may be able to remove check-cast instructions because we may
+ // have more information about the types of the arguments at the call site. This is
+ // particularly important for bridge methods.
+ codeRewriter.removeCasts(code);
+ // If a method was inlined we may be able to prune additional branches.
+ codeRewriter.simplifyIf(code);
+ }
}
private boolean isClassEligible(AppInfo appInfo, DexClass clazz) {
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 9145de1..bfb2b95 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo.ResolutionResult;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Descriptor;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexApplication;
@@ -95,7 +96,7 @@
private boolean tracingMainDex = false;
private final AppInfoWithSubtyping appInfo;
- private final GraphLense graphLense;
+ private final AppView<? extends AppInfoWithSubtyping> appView;
private final InternalOptions options;
private RootSet rootSet;
@@ -220,25 +221,34 @@
*/
private final ProguardConfiguration.Builder compatibility;
- public Enqueuer(
- AppInfoWithSubtyping appInfo,
- GraphLense graphLense,
- InternalOptions options,
- boolean forceProguardCompatibility) {
- this(appInfo, graphLense, options, forceProguardCompatibility, null);
+ public Enqueuer(AppView<? extends AppInfoWithSubtyping> appView, InternalOptions options) {
+ this(appView, options, options.forceProguardCompatibility, null);
}
public Enqueuer(
- AppInfoWithSubtyping appInfo,
- GraphLense graphLense,
+ AppView<? extends AppInfoWithSubtyping> appView,
+ InternalOptions options,
+ ProguardConfiguration.Builder compatibility) {
+ this(appView, options, options.forceProguardCompatibility, compatibility);
+ }
+
+ public Enqueuer(
+ AppView<? extends AppInfoWithSubtyping> appView,
+ InternalOptions options,
+ boolean forceProguardCompatibility) {
+ this(appView, options, forceProguardCompatibility, null);
+ }
+
+ public Enqueuer(
+ AppView<? extends AppInfoWithSubtyping> appView,
InternalOptions options,
boolean forceProguardCompatibility,
ProguardConfiguration.Builder compatibility) {
- this.appInfo = appInfo;
- this.graphLense = graphLense;
+ this.appInfo = appView.appInfo();
+ this.appView = appView;
this.compatibility = compatibility;
- this.options = options;
this.forceProguardCompatibility = forceProguardCompatibility;
+ this.options = options;
}
private void enqueueRootItems(Map<DexDefinition, ProguardKeepRule> items) {
@@ -1272,7 +1282,7 @@
numOfLiveItemsAfterProcessing += (long) liveFields.items.size();
if (numOfLiveItemsAfterProcessing > numOfLiveItems) {
RootSetBuilder consequentSetBuilder =
- new RootSetBuilder(appInfo, rootSet.ifRules, options);
+ new RootSetBuilder(appView, rootSet.ifRules, options);
ConsequentRootSet consequentRootSet = consequentSetBuilder.runForIfRules(
executorService, liveTypes, liveMethods.getItems(), liveFields.getItems());
enqueueRootItems(consequentRootSet.noShrinking);
@@ -1518,7 +1528,7 @@
private void handleReflectiveBehavior(DexEncodedMethod method) {
DexType originHolder = method.method.holder;
Origin origin = appInfo.originFor(originHolder);
- IRCode code = method.buildIR(appInfo, graphLense, options, origin);
+ IRCode code = method.buildIR(appInfo, appView.graphLense(), options, origin);
code.instructionIterator().forEachRemaining(this::handleReflectiveBehavior);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index 5d610a8..2aff5df 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -320,11 +320,12 @@
}
protected StringBuilder append(StringBuilder builder, boolean includeMemberRules) {
- StringUtils.appendNonEmpty(builder, "@", classAnnotation, null);
- StringUtils.appendNonEmpty(builder, "", classAccessFlags, null);
- StringUtils.appendNonEmpty(builder, "!", negatedClassAccessFlags.toString().replace(" ", " !"),
- null);
- if (builder.length() > 0) {
+ boolean needsSpaceBeforeClassType =
+ StringUtils.appendNonEmpty(builder, "@", classAnnotation, null)
+ | StringUtils.appendNonEmpty(builder, "", classAccessFlags, null)
+ | StringUtils.appendNonEmpty(
+ builder, "!", negatedClassAccessFlags.toString().replace(" ", " !"), null);
+ if (needsSpaceBeforeClassType) {
builder.append(' ');
}
if (classTypeNegated) {
@@ -339,12 +340,12 @@
builder.append(' ');
builder.append(inheritanceClassName);
}
- if (includeMemberRules) {
- builder.append(" {\n");
+ if (includeMemberRules && !memberRules.isEmpty()) {
+ builder.append(" {").append(System.lineSeparator());
memberRules.forEach(memberRule -> {
builder.append(" ");
builder.append(memberRule);
- builder.append(";\n");
+ builder.append(";").append(System.lineSeparator());
});
builder.append("}");
}
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 30def9e..e6f6a17 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
@@ -51,7 +52,7 @@
public class RootSetBuilder {
- private final AppInfo appInfo;
+ private final AppView<? extends AppInfo> appView;
private final DirectMappedDexApplication application;
private final Collection<ProguardConfigurationRule> rules;
private final Map<DexDefinition, ProguardKeepRule> noShrinking = new IdentityHashMap<>();
@@ -76,22 +77,20 @@
private final Set<ProguardIfRule> ifRules = Sets.newIdentityHashSet();
public RootSetBuilder(
- AppInfo appInfo,
+ AppView<? extends AppInfo> appView,
DexApplication application,
List<ProguardConfigurationRule> rules,
InternalOptions options) {
- this.appInfo = appInfo;
+ this.appView = appView;
this.application = application.asDirect();
this.rules = rules == null ? null : Collections.unmodifiableCollection(rules);
this.options = options;
}
RootSetBuilder(
- AppInfo appInfo,
- Set<ProguardIfRule> ifRules,
- InternalOptions options) {
- this.appInfo = appInfo;
- this.application = appInfo.app.asDirect();
+ AppView<? extends AppInfo> appView, Set<ProguardIfRule> ifRules, InternalOptions options) {
+ this.appView = appView;
+ this.application = appView.appInfo().app.asDirect();
this.rules = Collections.unmodifiableCollection(ifRules);
this.options = options;
}
@@ -341,13 +340,14 @@
Set<DexEncodedMethod> liveMethods,
Set<DexEncodedField> liveFields) throws ExecutionException {
application.timing.begin("Find consequent items for -if rules...");
- Function<DexType, DexClass> definitionForWithLiveTypes = type -> {
- DexClass clazz = appInfo.definitionFor(type);
- if (clazz != null && liveTypes.contains(clazz.type)) {
- return clazz;
- }
- return null;
- };
+ Function<DexType, DexClass> definitionForWithLiveTypes =
+ type -> {
+ DexClass clazz = appView.appInfo().definitionFor(type);
+ if (clazz != null && liveTypes.contains(clazz.type)) {
+ return clazz;
+ }
+ return null;
+ };
try {
List<Future<?>> futures = new ArrayList<>();
if (rules != null) {
@@ -358,7 +358,7 @@
// -keep rule may vary (due to back references). So, we need to try all pairs of -if rule
// and live types.
for (DexType currentLiveType : liveTypes) {
- DexClass currentLiveClass = appInfo.definitionFor(currentLiveType);
+ DexClass currentLiveClass = appView.appInfo().definitionFor(currentLiveType);
if (currentLiveClass == null) {
continue;
}
@@ -550,7 +550,7 @@
out.close();
}
- private static boolean satisfyClassType(ProguardConfigurationRule rule, DexClass clazz) {
+ private boolean satisfyClassType(ProguardConfigurationRule rule, DexClass clazz) {
return rule.getClassType().matches(clazz) != rule.getClassTypeNegated();
}
@@ -723,7 +723,7 @@
if (type.isPrimitiveType()) {
return;
}
- DexClass definition = appInfo.definitionFor(type);
+ DexClass definition = appView.appInfo().definitionFor(type);
if (definition == null || definition.isLibraryClass()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 4f0aa2c..244639e 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -59,9 +59,10 @@
return builder.toString();
}
- public static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
+ public static boolean appendNonEmpty(
+ StringBuilder builder, String pre, Object item, String post) {
if (item == null) {
- return;
+ return false;
}
String text = item.toString();
if (!text.isEmpty()) {
@@ -72,7 +73,9 @@
if (post != null) {
builder.append(post);
}
+ return true;
}
+ return false;
}
public static StringBuilder appendIndent(StringBuilder builder, String subject, int indent) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java
new file mode 100644
index 0000000..d52725e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.checkcast;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class RemoveCheckCastAfterClassInlining extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ AndroidApp input = readClasses(Lambda.class, Lambda.Consumer.class);
+ AndroidApp output =
+ compileWithR8(
+ input,
+ keepMainProguardConfiguration(Lambda.class),
+ options -> options.enableMinification = false);
+
+ // Extract main method.
+ CodeInspector inspector = new CodeInspector(output);
+ ClassSubject classSubject = inspector.clazz(Lambda.class);
+ MethodSubject methodSubject = classSubject.mainMethod();
+ assertThat(methodSubject, isPresent());
+
+ DexEncodedMethod method = methodSubject.getMethod();
+ assertTrue(method.hasCode());
+
+ DexCode code = method.getCode().asDexCode();
+ int numberOfConstStringInstructions = 0;
+ for (Instruction instruction : code.instructions) {
+ // Make sure that we do not load a const-string and then subsequently use a check-cast
+ // instruction to check if it is actually a string.
+ assertFalse(instruction.isCheckCast());
+ if (instruction.isConstString()) {
+ numberOfConstStringInstructions++;
+ }
+ }
+
+ // Sanity check that load() was actually inlined.
+ assertThat(
+ classSubject.method("void", "load", ImmutableList.of(Lambda.Consumer.class.getName())),
+ not(isPresent()));
+ assertEquals(2, numberOfConstStringInstructions);
+ }
+}
+
+class Lambda {
+
+ interface Consumer<T> {
+ void accept(T value);
+ }
+
+ public static void main(String... args) {
+ load(s -> System.out.println(s));
+ // Other code…
+ load(s -> System.out.println(s));
+ }
+
+ public static void load(Consumer<String> c) {
+ c.accept("Hello!");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index 932c09b..e340c98 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -75,20 +75,17 @@
ExecutorService executor = ThreadUtils.getExecutorService(1);
- AppInfoWithSubtyping appInfo = appView.appInfo();
- RootSet rootSet = new RootSetBuilder(appInfo, program, configuration.getRules(), options)
- .run(executor);
+ RootSet rootSet =
+ new RootSetBuilder(appView, program, configuration.getRules(), options).run(executor);
if (options.proguardConfiguration.isAccessModificationAllowed()) {
ClassAndMemberPublicizer.run(executor, timing, program, appView, rootSet);
rootSet =
- new RootSetBuilder(appInfo, program, configuration.getRules(), options).run(executor);
+ new RootSetBuilder(appView, program, configuration.getRules(), options).run(executor);
}
- Enqueuer enqueuer =
- new Enqueuer(
- appInfo, GraphLense.getIdentityLense(), options, options.forceProguardCompatibility);
- appInfo = enqueuer.traceApplication(rootSet, executor, timing);
+ Enqueuer enqueuer = new Enqueuer(appView, options, options.forceProguardCompatibility);
+ AppInfoWithSubtyping appInfo = enqueuer.traceApplication(rootSet, executor, timing);
return new Minifier(appInfo.withLiveness(), rootSet, Collections.emptySet(), options)
.run(timing);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index fbf4e68..deb91d8 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.AsmTestBase;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -102,18 +103,18 @@
InternalOptions options = new InternalOptions();
AndroidApp app = readClassesAndAsmDump(CLASSES, ASM_CLASSES);
DexApplication application = new ApplicationReader(app, options, timing).read().toDirect();
- AppInfoWithSubtyping appInfoWithSubtyping = new AppInfoWithSubtyping(application);
+ AppView<? extends AppInfoWithSubtyping> appView =
+ new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
ExecutorService executor = Executors.newSingleThreadExecutor();
- RootSet rootSet = new RootSetBuilder(appInfoWithSubtyping, application,
- buildKeepRuleForClass(Main.class, application.dexItemFactory), options).run(executor);
- appInfo =
- new Enqueuer(
- appInfoWithSubtyping,
- GraphLense.getIdentityLense(),
- options,
- options.forceProguardCompatibility)
- .traceApplication(rootSet, executor, timing);
+ RootSet rootSet =
+ new RootSetBuilder(
+ appView,
+ application,
+ buildKeepRuleForClass(Main.class, application.dexItemFactory),
+ options)
+ .run(executor);
+ appInfo = new Enqueuer(appView, options).traceApplication(rootSet, executor, timing);
// We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
// due to liveness.
}
diff --git a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
index 1a74018..1f06e72 100644
--- a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
@@ -33,7 +33,7 @@
String expected = String.join(System.lineSeparator(), ImmutableList.of(
"com.android.tools.r8.shaking.whyareyoukeeping.A",
"|- is live because referenced in keep rule:",
- "| -keep class com.android.tools.r8.shaking.whyareyoukeeping.A {",
+ "| -keep class com.android.tools.r8.shaking.whyareyoukeeping.A {",
"| *;",
"| };",
""));
diff --git a/tools/test.py b/tools/test.py
index 6c15ea5..b57d00f 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -23,32 +23,32 @@
def ParseOptions():
result = optparse.OptionParser()
- result.add_option('--no_internal',
+ result.add_option('--no-internal', '--no_internal',
help='Do not run Google internal tests.',
default=False, action='store_true')
- result.add_option('--archive_failures',
+ result.add_option('--archive-failures', '--archive_failures',
help='Upload test results to cloud storage on failure.',
default=False, action='store_true')
- result.add_option('--only_internal',
+ result.add_option('--only-internal', '--only_internal',
help='Only run Google internal tests.',
default=False, action='store_true')
- result.add_option('--all_tests',
+ result.add_option('--all-tests', '--all_tests',
help='Run tests in all configurations.',
default=False, action='store_true')
result.add_option('-v', '--verbose',
help='Print test stdout to, well, stdout.',
default=False, action='store_true')
- result.add_option('--dex_vm',
+ result.add_option('--dex-vm', '--dex_vm',
help='The android version of the vm to use. "all" will run the tests on '
'all art vm versions (stopping after first failed execution)',
default="default",
choices=ALL_ART_VMS + ["all"])
- result.add_option('--dex_vm_kind',
+ result.add_option('--dex-vm-kind', '--dex_vm_kind',
help='Whether to use host or target version of runtime',
default="host",
nargs=1,
choices=["host", "target"])
- result.add_option('--one_line_per_test',
+ result.add_option('--one-line-per-test', '--one_line_per_test',
help='Print a line before a tests starts and after it ends to stdout.',
default=False, action='store_true')
result.add_option('--tool',
@@ -58,32 +58,32 @@
result.add_option('--jctf',
help='Run JCTF tests with: "r8" (default) or "d8".',
default=False, action='store_true')
- result.add_option('--only_jctf',
+ result.add_option('--only-jctf', '--only_jctf',
help='Run only JCTF tests with: "r8" (default) or "d8".',
default=False, action='store_true')
- result.add_option('--jctf_compile_only',
+ result.add_option('--jctf-compile-only', '--jctf_compile_only',
help="Don't run, only compile JCTF tests.",
default=False, action='store_true')
- result.add_option('--aosp_jar',
+ result.add_option('--aosp-jar', '--aosp_jar',
help='Run aosp_jar test.',
default=False, action='store_true')
- result.add_option('--disable_assertions',
+ result.add_option('--disable-assertions', '--disable_assertions',
help='Disable assertions when running tests.',
default=False, action='store_true')
- result.add_option('--with_code_coverage',
+ result.add_option('--with-code-coverage', '--with_code_coverage',
help='Enable code coverage with Jacoco.',
default=False, action='store_true')
- result.add_option('--test_dir',
+ result.add_option('--test-dir', '--test_dir',
help='Use a custom directory for the test artifacts instead of a'
' temporary (which is automatically removed after the test).'
' Note that the directory will not be cleared before the test.')
- result.add_option('--java_home',
+ result.add_option('--java-home', '--java_home',
help='Use a custom java version to run tests.')
- result.add_option('--generate_golden_files_to',
+ result.add_option('--generate-golden-files-to', '--generate_golden_files_to',
help='Store dex files produced by tests in the specified directory.'
' It is aimed to be read on platforms with no host runtime available'
' for comparison.')
- result.add_option('--use_golden_files_in',
+ result.add_option('--use-golden-files-in', '--use_golden_files_in',
help='Download golden files hierarchy for this commit in the specified'
' location and use them instead of executing on host runtime.')