Merge "Collect newly added SSA values by LensCodeRewriter."
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/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index fa35d0d..6f93df4 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.SmaliWriter;
@@ -711,7 +710,7 @@
protected ProcessResult runOnVMRaw(AndroidApp app, Class<?> mainClass, Backend backend)
throws IOException {
- return runOnVMRaw(app, mainClass.getCanonicalName(), backend);
+ return runOnVMRaw(app, mainClass.getTypeName(), backend);
}
protected ProcessResult runOnVMRaw(AndroidApp app, String mainClass, Backend backend)
@@ -830,13 +829,6 @@
.map(kind::cast);
}
- protected Stream<CfInstruction> filterInstructionKind(
- CfCode code, Class<? extends CfInstruction> kind) {
- return code.getInstructions().stream()
- .filter(kind::isInstance)
- .map(kind::cast);
- }
-
public enum MinifyMode {
NONE,
JAVA,
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index f5eaa25..481c50c 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -949,7 +949,7 @@
}
public static ProcessResult runJava(Class clazz) throws Exception {
- String main = clazz.getCanonicalName();
+ String main = clazz.getTypeName();
Path path = getClassPathForTests();
return runJava(path, main);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java
index 44720ed..1eddbd3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java
@@ -13,19 +13,18 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.cf.code.CfCheckCast;
import com.android.tools.r8.debug.CfDebugTestConfig;
import com.android.tools.r8.debug.DebugTestBase;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.debug.DexDebugTestConfig;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.code.CheckCast;
import com.android.tools.r8.origin.Origin;
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.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
@@ -82,7 +81,8 @@
ClassSubject classSubject = inspector.clazz(MAIN);
MethodSubject method = classSubject.method("void", "differentLocals", ImmutableList.of());
assertThat(method, isPresent());
- long count = countCheckCast(method.getMethod().getCode());
+ long count =
+ Streams.stream(method.iterateInstructions(InstructionSubject::isCheckCast)).count();
assertEquals(1, count);
DebugTestConfig config = backend == Backend.CF
@@ -134,7 +134,8 @@
ClassSubject classSubject = inspector.clazz(MAIN);
MethodSubject method = classSubject.method("void", "sameLocal", ImmutableList.of());
assertThat(method, isPresent());
- long count = countCheckCast(method.getMethod().getCode());
+ long count =
+ Streams.stream(method.iterateInstructions(InstructionSubject::isCheckCast)).count();
assertEquals(1, count);
DebugTestConfig config = backend == Backend.CF
@@ -166,13 +167,4 @@
);
}
- private long countCheckCast(Code code) {
- if (backend == Backend.DEX) {
- return filterInstructionKind(code.asDexCode(), CheckCast.class).count();
- } else {
- assert backend == Backend.CF;
- return filterInstructionKind(code.asCfCode(), CfCheckCast.class).count();
- }
- }
-
}
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/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 67afdd7..6b7a7c2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -10,7 +10,6 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.code.InvokeStatic;
import com.android.tools.r8.code.NewInstance;
import com.android.tools.r8.code.SgetObject;
import com.android.tools.r8.graph.DexClass;
@@ -19,9 +18,12 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -229,8 +231,8 @@
private List<String> collectStaticCalls(ClassSubject clazz, String methodName, String... params) {
assertNotNull(clazz);
MethodSignature signature = new MethodSignature(methodName, "void", params);
- DexCode code = clazz.method(signature).getMethod().getCode().asDexCode();
- return filterInstructionKind(code, InvokeStatic.class)
+ MethodSubject method = clazz.method(signature);
+ return Streams.stream(method.iterateInstructions(InstructionSubject::isInvokeStatic))
.map(insn -> insn.getMethod().toSourceString())
.sorted()
.collect(Collectors.toList());
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java b/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java
new file mode 100644
index 0000000..b280242
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java
@@ -0,0 +1,95 @@
+// 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.memberrebinding;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidAppConsumers;
+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 IndirectSuperInterfaceTest extends TestBase {
+
+ public interface Interface {
+ @NeverInline
+ default void foo() {
+ System.out.print("Interface::foo ");
+ }
+ }
+
+ public static class A implements Interface {
+ // Intentionally empty.
+ }
+
+ public static class B extends A {
+ @Override
+ public void foo() {
+ System.out.print("B::foo ");
+ super.foo();
+ }
+
+ public static void main(String[] args) {
+ new B().foo();
+ }
+ }
+
+ private final Backend backend;
+
+ @Parameters(name = "{0}")
+ public static Backend[] setup() {
+ return new Backend[] {Backend.CF, Backend.DEX};
+ }
+
+ public IndirectSuperInterfaceTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expected = "B::foo Interface::foo ";
+ String reference = runOnJava(B.class);
+ assertEquals(expected, reference);
+
+ AndroidAppConsumers sink = new AndroidAppConsumers();
+ Builder builder =
+ R8Command.builder()
+ .addClassProgramData(ToolHelper.getClassAsBytes(Interface.class), Origin.unknown())
+ .addClassProgramData(ToolHelper.getClassAsBytes(A.class), Origin.unknown())
+ .addClassProgramData(ToolHelper.getClassAsBytes(B.class), Origin.unknown())
+ .setProgramConsumer(sink.wrapProgramConsumer(emptyConsumer(backend)))
+ .addLibraryFiles(runtimeJar(backend))
+ .addProguardConfiguration(
+ ImmutableList.of(
+ "-keep class " + Interface.class.getTypeName(),
+ "-keep class " + A.class.getTypeName(),
+ keepMainProguardConfigurationWithInliningAnnotation(B.class)),
+ Origin.unknown());
+ ToolHelper.allowTestProguardOptions(builder);
+ if (backend == Backend.DEX) {
+ builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+ }
+ R8.run(builder.build());
+
+ ProcessResult result = runOnVMRaw(sink.build(), B.class, backend);
+
+ // TODO(b/117407667): Assert the test does not fail once fixed.
+ assertTrue(result.toString(), result.exitCode == (backend == Backend.DEX ? 0 : 1));
+ if (result.exitCode == 0) {
+ assertEquals(reference, result.stdout);
+ }
+ }
+}
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/forceproguardcompatibility/ProguardCompatibilityTestBase.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatibilityTestBase.java
index 24986e1..44527a2 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatibilityTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatibilityTestBase.java
@@ -63,6 +63,14 @@
|| shrinker == Shrinker.R8_CF;
}
+ protected static Backend toBackend(Shrinker shrinker) {
+ if (generatesDex(shrinker)) {
+ return Backend.DEX;
+ }
+ assert generatesCf(shrinker);
+ return Backend.CF;
+ }
+
protected AndroidApp runShrinker(
Shrinker mode, List<Class> programClasses, Iterable<String> proguardConfigs)
throws Exception {
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ExternalizableTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ExternalizableTest.java
new file mode 100644
index 0000000..fe2a93f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ExternalizableTest.java
@@ -0,0 +1,345 @@
+// 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.shaking.forceproguardcompatibility.defaultctor;
+
+import static com.android.tools.r8.shaking.forceproguardcompatibility.defaultctor.ExternalizableDataClass.TYPE_1;
+import static com.android.tools.r8.shaking.forceproguardcompatibility.defaultctor.ExternalizableDataClass.TYPE_2;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
+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 java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+final class ExternalizableDataClass implements Externalizable {
+ static final byte TYPE_1 = 1;
+ static final byte TYPE_2 = 2;
+
+ private byte byteField;
+ private Object objectField;
+
+ // Default constructor for deserialization
+ public ExternalizableDataClass() {
+ }
+
+ // Constructor for serialization
+ public ExternalizableDataClass(byte byteField, Object objectField) {
+ this.byteField = byteField;
+ this.objectField = objectField;
+ }
+
+ @Override
+ public void writeExternal(ObjectOutput objectOutput) throws IOException {
+ writeInternal(byteField, objectField, objectOutput);
+ }
+
+ private static void writeInternal(byte b, Object obj, DataOutput out) throws IOException {
+ out.writeByte(b);
+ switch (b) {
+ case TYPE_1:
+ ((Delegate1) obj).delegateWrite(out);
+ break;
+ case TYPE_2:
+ ((Delegate2) obj).delegateWrite(out);
+ break;
+ default:
+ throw new InvalidClassException("Unknown type: " + b);
+ }
+ }
+
+ @Override
+ public void readExternal(ObjectInput objectInput) throws IOException {
+ byteField = objectInput.readByte();
+ objectField = readInternal(byteField, objectInput);
+ }
+
+ private static Object readInternal(byte type, DataInput in) throws IOException {
+ switch (type) {
+ case TYPE_1: return Delegate1.delegateRead(in);
+ case TYPE_2: return Delegate2.delegateRead(in);
+ default:
+ throw new InvalidClassException("Unknown type: " + type);
+ }
+ }
+
+ private Object readResolve() {
+ return objectField;
+ }
+
+ @Override
+ public String toString() {
+ return "{ type: " + byteField + ", obj: " + objectField.toString() + " }";
+ }
+}
+
+final class Delegate1 implements Serializable {
+ private int intField;
+
+ private Object writeReplace() {
+ return new ExternalizableDataClass(TYPE_1, this);
+ }
+
+ static Delegate1 of(int intField) {
+ Delegate1 instance = new Delegate1();
+ instance.intField = intField;
+ return instance;
+ }
+
+ void delegateWrite(DataOutput out) throws IOException {
+ out.writeInt(intField);
+ }
+
+ static Object delegateRead(DataInput in) throws IOException {
+ int i = in.readInt();
+ return Delegate1.of(i);
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ @Override
+ public String toString() {
+ return "(" + intField + ")";
+ }
+}
+
+final class Delegate2 implements Serializable {
+ private String stringField;
+
+ private Object writeReplace() {
+ return new ExternalizableDataClass(TYPE_2, this);
+ }
+
+ static Delegate2 of(String stringField) {
+ Delegate2 instance = new Delegate2();
+ instance.stringField = stringField;
+ return instance;
+ }
+
+ void delegateWrite(DataOutput out) throws IOException {
+ out.writeUTF(stringField);
+ }
+
+ static Object delegateRead(DataInput in) throws IOException {
+ String s = in.readUTF();
+ return Delegate2.of(s);
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ @Override
+ public String toString() {
+ return "<" + stringField + ">";
+ }
+}
+
+class ExternalizableTestMain {
+ public static void main(String[] args) throws Exception {
+ Delegate2 data2 = Delegate2.of("MessageToSerialize");
+ // "Before: <MessageToSerialize>"
+ System.out.println("Before: " + data2.toString());
+
+ // Serialization
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
+ objectOutputStream.writeObject(data2);
+ objectOutputStream.close();
+
+ byte[] byteArray = out.toByteArray();
+
+ // Deserialization
+ ByteArrayInputStream in = new ByteArrayInputStream(byteArray);
+ ObjectInputStream objectInputStream = new ObjectInputStream(in);
+ Object copy = objectInputStream.readObject();
+ assert copy instanceof Delegate2;
+ // "After: <MessageToSerialize>"
+ System.out.println("After: " + copy.toString());
+ }
+}
+
+class NonSerializableSuperClass {
+ protected String tag;
+
+ // Default constructor for deserialization
+ public NonSerializableSuperClass() {
+ this.tag = null;
+ }
+
+ public NonSerializableSuperClass(String tag) {
+ this.tag = tag;
+ }
+
+ @Override
+ public String toString() {
+ return tag == null ? "NULL" : tag;
+ }
+}
+
+class SerializableDataClass extends NonSerializableSuperClass implements Serializable {
+ private String extraTag;
+
+ public SerializableDataClass() {
+ super();
+ this.extraTag = null;
+ }
+
+ public SerializableDataClass(String tag, String extraTag) {
+ super(tag);
+ this.extraTag = extraTag;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + ", " + (extraTag == null ? "NULL" : extraTag);
+ }
+}
+
+class SerializableTestMain {
+ public static void main(String[] args) throws Exception {
+ SerializableDataClass data = new SerializableDataClass("TagToSerialize", "ExtraToSerialize");
+ // "Before: TagToSerialize, ExtraToSerialize"
+ System.out.println("Before: " + data.toString());
+
+ // Serialization
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
+ objectOutputStream.writeObject(data);
+ objectOutputStream.close();
+
+ byte[] byteArray = out.toByteArray();
+
+ // Deserialization
+ ByteArrayInputStream in = new ByteArrayInputStream(byteArray);
+ ObjectInputStream objectInputStream = new ObjectInputStream(in);
+ Object copy = objectInputStream.readObject();
+ assert copy instanceof SerializableDataClass;
+ // "After: NULL, ExtraToSerialize"
+ System.out.println("After: " + copy.toString());
+ }
+}
+
+@RunWith(Parameterized.class)
+public class ExternalizableTest extends ProguardCompatibilityTestBase {
+ private final static List<Class> CLASSES_FOR_EXTERNALIZABLE = ImmutableList.of(
+ ExternalizableDataClass.class, Delegate1.class, Delegate2.class, ExternalizableTestMain.class
+ );
+
+ private final static List<Class> CLASSES_FOR_SERIALIZABLE = ImmutableList.of(
+ NonSerializableSuperClass.class, SerializableDataClass.class, SerializableTestMain.class
+ );
+
+ private final Shrinker shrinker;
+
+ @Parameterized.Parameters(name = "Shrinker: {0}")
+ public static Collection<Object> data() {
+ return ImmutableList.of(
+ Shrinker.PROGUARD6_THEN_D8, Shrinker.PROGUARD6, Shrinker.R8, Shrinker.R8_CF);
+ }
+
+ public ExternalizableTest(Shrinker shrinker) {
+ this.shrinker = shrinker;
+ }
+
+ @Test
+ public void testExternalizable() throws Exception {
+ // TODO(b/116735204): R8 should keep default ctor() of classes that implement Externalizable
+ if (isR8(shrinker)) {
+ return;
+ }
+
+ String javaOutput = runOnJava(ExternalizableTestMain.class);
+
+ List<String> config = ImmutableList.of(
+ keepMainProguardConfiguration(ExternalizableTestMain.class),
+ // https://www.guardsquare.com/en/products/proguard/manual/examples#serializable
+ "-keepclassmembers class * implements java.io.Serializable {",
+ //" private static final java.io.ObjectStreamField[] serialPersistentFields;",
+ " private void writeObject(java.io.ObjectOutputStream);",
+ " private void readObject(java.io.ObjectInputStream);",
+ " java.lang.Object writeReplace();",
+ " java.lang.Object readResolve();",
+ "}");
+
+ AndroidApp processedApp = runShrinker(shrinker, CLASSES_FOR_EXTERNALIZABLE, config);
+
+ // TODO(b/117302947): Need to update ART binary.
+ if (generatesCf(shrinker)) {
+ String output = runOnVM(
+ processedApp, ExternalizableTestMain.class.getCanonicalName(), toBackend(shrinker));
+ assertEquals(javaOutput.trim(), output.trim());
+ }
+
+ CodeInspector codeInspector = new CodeInspector(processedApp, proguardMap);
+ ClassSubject classSubject = codeInspector.clazz(ExternalizableDataClass.class);
+ assertThat(classSubject, isPresent());
+ MethodSubject init = classSubject.init(ImmutableList.of());
+ assertThat(init, isPresent());
+ }
+
+ @Test
+ public void testSerializable() throws Exception {
+ // TODO(b/116735204): R8 should keep default ctor() of first non-serializable superclass of
+ // serializable class.
+ if (isR8(shrinker)) {
+ return;
+ }
+
+ String javaOutput = runOnJava(SerializableTestMain.class);
+
+ List<String> config = ImmutableList.of(
+ keepMainProguardConfiguration(SerializableTestMain.class),
+ // https://www.guardsquare.com/en/products/proguard/manual/examples#serializable
+ "-keepclassmembers class * implements java.io.Serializable {",
+ //" private static final java.io.ObjectStreamField[] serialPersistentFields;",
+ " private void writeObject(java.io.ObjectOutputStream);",
+ " private void readObject(java.io.ObjectInputStream);",
+ " java.lang.Object writeReplace();",
+ " java.lang.Object readResolve();",
+ "}");
+
+ AndroidApp processedApp = runShrinker(shrinker, CLASSES_FOR_SERIALIZABLE, config);
+ // TODO(b/117302947): Need to update ART binary.
+ if (generatesCf(shrinker)) {
+ String output = runOnVM(
+ processedApp, SerializableTestMain.class.getCanonicalName(), toBackend(shrinker));
+ assertEquals(javaOutput.trim(), output.trim());
+ }
+
+ CodeInspector codeInspector = new CodeInspector(processedApp, proguardMap);
+ ClassSubject classSubject = codeInspector.clazz(NonSerializableSuperClass.class);
+ assertThat(classSubject, isPresent());
+ MethodSubject init = classSubject.init(ImmutableList.of());
+ assertThat(init, isPresent());
+ }
+}
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/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 3203f9c..dedb839 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -24,6 +24,8 @@
import com.android.tools.r8.cf.code.CfStackInstruction;
import com.android.tools.r8.cf.code.CfSwitch;
import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.ValueType;
import org.objectweb.asm.Opcodes;
@@ -40,6 +42,41 @@
}
@Override
+ public boolean isInstancePut() {
+ return instruction instanceof CfFieldInstruction
+ && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.PUTFIELD;
+ }
+
+ @Override
+ public boolean isStaticPut() {
+ return instruction instanceof CfFieldInstruction
+ && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.PUTSTATIC;
+ }
+
+ @Override
+ public boolean isInstanceGet() {
+ return instruction instanceof CfFieldInstruction
+ && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.GETFIELD;
+ }
+
+ @Override
+ public boolean isStaticGet() {
+ return instruction instanceof CfFieldInstruction
+ && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.GETSTATIC;
+ }
+
+ @Override
+ public DexField getField() {
+ assert isFieldAccess();
+ return ((CfFieldInstruction) instruction).getField();
+ }
+
+ @Override
+ public boolean isInvoke() {
+ return instruction instanceof CfInvoke || instruction instanceof CfInvokeDynamic;
+ }
+
+ @Override
public boolean isInvokeVirtual() {
return instruction instanceof CfInvoke
&& ((CfInvoke) instruction).getOpcode() == Opcodes.INVOKEVIRTUAL;
@@ -58,6 +95,12 @@
}
@Override
+ public DexMethod getMethod() {
+ assert isInvoke();
+ return ((CfInvoke) instruction).getMethod();
+ }
+
+ @Override
public boolean isNop() {
return instruction instanceof CfNop;
}
@@ -105,40 +148,11 @@
}
@Override
- public boolean isInvoke() {
- return instruction instanceof CfInvoke || instruction instanceof CfInvokeDynamic;
- }
-
- @Override
public boolean isNewInstance() {
return instruction instanceof CfNew;
}
@Override
- public boolean isInstancePut() {
- return instruction instanceof CfFieldInstruction
- && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.PUTFIELD;
- }
-
- @Override
- public boolean isStaticPut() {
- return instruction instanceof CfFieldInstruction
- && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.PUTSTATIC;
- }
-
- @Override
- public boolean isInstanceGet() {
- return instruction instanceof CfFieldInstruction
- && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.GETFIELD;
- }
-
- @Override
- public boolean isStaticGet() {
- return instruction instanceof CfFieldInstruction
- && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.GETSTATIC;
- }
-
- @Override
public boolean isCheckCast() {
return instruction instanceof CfCheckCast;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 4ea699a..e1d74c1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -67,6 +67,8 @@
import com.android.tools.r8.code.SputShort;
import com.android.tools.r8.code.SputWide;
import com.android.tools.r8.code.Throw;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
public class DexInstructionSubject implements InstructionSubject {
protected final Instruction instruction;
@@ -81,6 +83,65 @@
}
@Override
+ public boolean isInstanceGet() {
+ return instruction instanceof Iget
+ || instruction instanceof IgetBoolean
+ || instruction instanceof IgetByte
+ || instruction instanceof IgetShort
+ || instruction instanceof IgetChar
+ || instruction instanceof IgetWide
+ || instruction instanceof IgetObject;
+ }
+
+ @Override
+ public boolean isInstancePut() {
+ return instruction instanceof Iput
+ || instruction instanceof IputBoolean
+ || instruction instanceof IputByte
+ || instruction instanceof IputShort
+ || instruction instanceof IputChar
+ || instruction instanceof IputWide
+ || instruction instanceof IputObject;
+ }
+
+ @Override
+ public boolean isStaticGet() {
+ return instruction instanceof Sget
+ || instruction instanceof SgetBoolean
+ || instruction instanceof SgetByte
+ || instruction instanceof SgetShort
+ || instruction instanceof SgetChar
+ || instruction instanceof SgetWide
+ || instruction instanceof SgetObject;
+ }
+
+ @Override
+ public boolean isStaticPut() {
+ return instruction instanceof Sput
+ || instruction instanceof SputBoolean
+ || instruction instanceof SputByte
+ || instruction instanceof SputShort
+ || instruction instanceof SputChar
+ || instruction instanceof SputWide
+ || instruction instanceof SputObject;
+ }
+
+ @Override
+ public DexField getField() {
+ assert isFieldAccess();
+ return instruction.getField();
+ }
+
+ @Override
+ public boolean isInvoke() {
+ return isInvokeVirtual()
+ || isInvokeInterface()
+ || isInvokeDirect()
+ || isInvokeSuper()
+ || isInvokeStatic();
+ }
+
+ @Override
public boolean isInvokeVirtual() {
return instruction instanceof InvokeVirtual || instruction instanceof InvokeVirtualRange;
}
@@ -95,6 +156,20 @@
return instruction instanceof InvokeStatic || instruction instanceof InvokeStaticRange;
}
+ public boolean isInvokeSuper() {
+ return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange;
+ }
+
+ public boolean isInvokeDirect() {
+ return instruction instanceof InvokeDirect || instruction instanceof InvokeDirectRange;
+ }
+
+ @Override
+ public DexMethod getMethod() {
+ assert isInvoke();
+ return instruction.getMethod();
+ }
+
@Override
public boolean isNop() {
return instruction instanceof Nop;
@@ -147,15 +222,6 @@
}
@Override
- public boolean isInvoke() {
- return isInvokeVirtual()
- || isInvokeInterface()
- || isInvokeDirect()
- || isInvokeSuper()
- || isInvokeStatic();
- }
-
- @Override
public boolean isNewInstance() {
return instruction instanceof NewInstance;
}
@@ -170,63 +236,11 @@
return isCheckCast() && ((CheckCast) instruction).getType().toString().equals(type);
}
- public boolean isInvokeSuper() {
- return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange;
- }
-
- public boolean isInvokeDirect() {
- return instruction instanceof InvokeDirect || instruction instanceof InvokeDirectRange;
- }
-
public boolean isConst4() {
return instruction instanceof Const4;
}
@Override
- public boolean isInstanceGet() {
- return instruction instanceof Iget
- || instruction instanceof IgetBoolean
- || instruction instanceof IgetByte
- || instruction instanceof IgetShort
- || instruction instanceof IgetChar
- || instruction instanceof IgetWide
- || instruction instanceof IgetObject;
- }
-
- @Override
- public boolean isInstancePut() {
- return instruction instanceof Iput
- || instruction instanceof IputBoolean
- || instruction instanceof IputByte
- || instruction instanceof IputShort
- || instruction instanceof IputChar
- || instruction instanceof IputWide
- || instruction instanceof IputObject;
- }
-
- @Override
- public boolean isStaticGet() {
- return instruction instanceof Sget
- || instruction instanceof SgetBoolean
- || instruction instanceof SgetByte
- || instruction instanceof SgetShort
- || instruction instanceof SgetChar
- || instruction instanceof SgetWide
- || instruction instanceof SgetObject;
- }
-
- @Override
- public boolean isStaticPut() {
- return instruction instanceof Sput
- || instruction instanceof SputBoolean
- || instruction instanceof SputByte
- || instruction instanceof SputShort
- || instruction instanceof SputChar
- || instruction instanceof SputWide
- || instruction instanceof SputObject;
- }
-
- @Override
public boolean isIf() {
return instruction instanceof IfEq
|| instruction instanceof IfEqz
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index e7eaa60..fb0e0d3 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.utils.codeinspector;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+
public interface InstructionSubject {
enum JumboStringMode {
@@ -13,12 +16,26 @@
boolean isFieldAccess();
+ boolean isInstancePut();
+
+ boolean isStaticPut();
+
+ boolean isInstanceGet();
+
+ boolean isStaticGet();
+
+ DexField getField();
+
+ boolean isInvoke();
+
boolean isInvokeVirtual();
boolean isInvokeInterface();
boolean isInvokeStatic();
+ DexMethod getMethod();
+
boolean isNop();
boolean isConstString(JumboStringMode jumboStringMode);
@@ -37,18 +54,8 @@
boolean isThrow();
- boolean isInvoke();
-
boolean isNewInstance();
- boolean isInstancePut();
-
- boolean isStaticPut();
-
- boolean isInstanceGet();
-
- boolean isStaticGet();
-
boolean isCheckCast();
boolean isCheckCast(String type);
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.')