Merge commit '52275f13b7965a7380c28c2449b662e3def10df4' into dev-release
diff --git a/.gitignore b/.gitignore
index ec03160..0629bac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -98,6 +98,8 @@
third_party/kotlin/kotlin-compiler-1.3.41
third_party/kotlin/kotlin-compiler-1.3.72.tar.gz
third_party/kotlin/kotlin-compiler-1.3.72
+third_party/kotlinx-coroutines-1.3.6.tar.gz
+third_party/kotlinx-coroutines-1.3.6
third_party/nest/*
third_party/openjdk/desugar_jdk_libs
third_party/openjdk/desugar_jdk_libs.tar.gz
diff --git a/build.gradle b/build.gradle
index 90ad4ab..338df88 100644
--- a/build.gradle
+++ b/build.gradle
@@ -327,6 +327,7 @@
"kotlin/kotlin-compiler-1.3.11",
"kotlin/kotlin-compiler-1.3.41",
"kotlin/kotlin-compiler-1.3.72",
+ "kotlinx-coroutines-1.3.6",
"openjdk/openjdk-rt-1.8",
"openjdk/desugar_jdk_libs",
"openjdk/jdk-11-test",
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index 50e5be1..9bfdfcd 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -8,7 +8,9 @@
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -134,14 +136,15 @@
* associated diagnostics handler and a {@link CompilationFailedException} exception is thrown.
*/
public final C build() throws CompilationFailedException {
- try {
- validate();
- C c = makeCommand();
- reporter.failIfPendingErrors();
- return c;
- } catch (AbortException e) {
- throw new CompilationFailedException(e);
- }
+ Box<C> box = new Box<>(null);
+ ExceptionUtils.withCompilationHandler(
+ reporter,
+ () -> {
+ validate();
+ box.set(makeCommand());
+ reporter.failIfPendingErrors();
+ });
+ return box.get();
}
// Helper to construct the actual command. Called as part of {@link build()}.
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d3a291f..8406706 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -684,7 +684,8 @@
appView,
new SubtypingInfo(application.allClasses(), application),
keptGraphConsumer,
- missingClasses);
+ missingClasses,
+ prunedTypes);
appView.setAppInfo(
enqueuer
.traceApplication(
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index d31606d..6220eb4 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -11,7 +11,11 @@
import com.google.common.collect.Iterables;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
public class R8CommandParser extends BaseCompilerCommandParser<R8Command, R8Command.Builder> {
@@ -24,12 +28,15 @@
MIN_API_FLAG,
"--main-dex-rules",
"--main-dex-list",
+ "--feature",
"--main-dex-list-output",
"--pg-conf",
"--pg-map-output",
"--desugared-lib",
THREAD_COUNT_FLAG);
+ private static final Set<String> OPTIONS_WITH_TWO_PARAMETERS = ImmutableSet.of("--feature");
+
public static void main(String[] args) throws CompilationFailedException {
R8Command command = parse(args, Origin.root()).build();
if (command.isPrintHelp()) {
@@ -83,6 +90,9 @@
" --main-dex-rules <file> # Proguard keep rules for classes to place in the",
" # primary dex file.",
" --main-dex-list <file> # List of classes to place in the primary dex file.",
+ " --feature <input> <output> ",
+ " # Add feature <input> file to <output> file. Several ",
+ " # occurrences can map to the same output.",
" --main-dex-list-output <file> ",
" # Output the full main-dex list in <file>."),
ASSERTIONS_USAGE_MESSAGE,
@@ -131,9 +141,11 @@
private void parse(
String[] args, Origin argsOrigin, R8Command.Builder builder, ParseState state) {
String[] expandedArgs = FlagFile.expandFlagFiles(args, builder::error);
+ Map<Path, List<Path>> featureSplitJars = new HashMap<>();
for (int i = 0; i < expandedArgs.length; i++) {
String arg = expandedArgs[i].trim();
String nextArg = null;
+ String nextNextArg = null;
if (OPTIONS_WITH_PARAMETER.contains(arg)) {
if (++i < expandedArgs.length) {
nextArg = expandedArgs[i];
@@ -143,6 +155,16 @@
"Missing parameter for " + expandedArgs[i - 1] + ".", argsOrigin));
break;
}
+ if (OPTIONS_WITH_TWO_PARAMETERS.contains(arg)) {
+ if (++i < expandedArgs.length) {
+ nextNextArg = expandedArgs[i];
+ } else {
+ builder.error(
+ new StringDiagnostic(
+ "Missing parameter for " + expandedArgs[i - 2] + ".", argsOrigin));
+ break;
+ }
+ }
}
if (arg.length() == 0) {
continue;
@@ -214,6 +236,10 @@
builder.setDisableDesugaring(true);
} else if (arg.equals("--main-dex-rules")) {
builder.addMainDexRulesFiles(Paths.get(nextArg));
+ } else if (arg.equals("--feature")) {
+ featureSplitJars
+ .computeIfAbsent(Paths.get(nextNextArg), k -> new ArrayList<>())
+ .add(Paths.get(nextArg));
} else if (arg.equals("--main-dex-list")) {
builder.addMainDexListFiles(Paths.get(nextArg));
} else if (arg.equals("--main-dex-list-output")) {
@@ -239,5 +265,20 @@
builder.addProgramFiles(Paths.get(arg));
}
}
+ featureSplitJars.forEach(
+ (outputPath, inputJars) -> addFeatureJar(builder, outputPath, inputJars));
+ }
+
+ public void addFeatureJar(R8Command.Builder builder, Path outputPath, List<Path> inputJarPaths) {
+ builder.addFeatureSplit(
+ featureSplitGenerator -> {
+ featureSplitGenerator.setProgramConsumer(
+ builder.createProgramOutputConsumer(outputPath, OutputMode.DexIndexed, true));
+ for (Path inputPath : inputJarPaths) {
+ featureSplitGenerator.addProgramResourceProvider(
+ ArchiveProgramResourceProvider.fromArchive(inputPath));
+ }
+ return featureSplitGenerator.build();
+ });
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index 854c5e7..788b358 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -38,6 +38,14 @@
public static OptionalBool isMethodAccessible(
DexEncodedMethod method,
DexClass holder,
+ ProgramMethod context,
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return isMethodAccessible(method, holder, context.getHolder(), appView.appInfo());
+ }
+
+ public static OptionalBool isMethodAccessible(
+ DexEncodedMethod method,
+ DexClass holder,
DexProgramClass context,
AppInfoWithClassHierarchy appInfo) {
return isMemberAccessible(method.accessFlags, holder, context, appInfo);
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 73395b2..7a85dc8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -42,6 +42,10 @@
this.staticValue = staticValue;
}
+ public DexType type() {
+ return field.type;
+ }
+
public boolean isProgramField(DexDefinitionSupplier definitions) {
if (field.holder.isClassType()) {
DexClass clazz = definitions.definitionFor(field.holder);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 3b635e6..1a318c0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1268,6 +1268,7 @@
public final DexMethod name;
public final DexMethod toString;
public final DexMethod compareTo;
+ public final DexMethod equals;
public final DexMethod constructor =
createMethod(enumType, createProto(voidType, stringType, intType), constructorMethodName);
@@ -1301,10 +1302,13 @@
DexString.EMPTY_ARRAY);
compareTo =
createMethod(
+ enumDescriptor, compareToMethodName, intDescriptor, new DexString[] {enumDescriptor});
+ equals =
+ createMethod(
enumDescriptor,
- compareToMethodName,
- stringDescriptor,
- new DexString[] {enumDescriptor});
+ equalsMethodName,
+ booleanDescriptor,
+ new DexString[] {objectDescriptor});
}
public boolean isValuesMethod(DexMethod method, DexClass enumClass) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index 978646c..41f5dc8 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -388,7 +388,7 @@
return false;
}
ResolutionResult resolutionResult =
- appView.appInfo().resolveMethodOn(superType, method, instruction.itf);
+ appView.appInfo().resolveMethodOn(superType, method, instruction.isInterface);
if (!resolutionResult.isSingleResolution()) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index 0e3f404..8b63913 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -162,10 +162,16 @@
public static DexType getRefinedReceiverType(
AppView<? extends AppInfoWithClassHierarchy> appView, DexMethod method, Value receiver) {
- TypeElement lattice = receiver.getDynamicUpperBoundType(appView);
+ return toRefinedReceiverType(receiver.getDynamicUpperBoundType(appView), method, appView);
+ }
+
+ public static DexType toRefinedReceiverType(
+ TypeElement receiverUpperBoundType,
+ DexMethod method,
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
DexType staticReceiverType = method.holder;
- if (lattice.isClassType()) {
- ClassTypeElement classType = lattice.asClassType();
+ if (receiverUpperBoundType.isClassType()) {
+ ClassTypeElement classType = receiverUpperBoundType.asClassType();
DexType refinedType = classType.getClassType();
if (refinedType == appView.dexItemFactory().objectType) {
Set<DexType> interfaces = classType.getInterfaces();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index c6012bd..117af7b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -12,50 +12,60 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.Assume.Assumption;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.utils.BooleanUtils;
import java.util.Objects;
import java.util.Set;
-public class Assume<An extends Assumption> extends Instruction {
+public class Assume extends Instruction {
private static final String ERROR_MESSAGE =
"Expected Assume instructions to be removed after IR processing.";
- private final An assumption;
+ private final DynamicTypeAssumption dynamicTypeAssumption;
+ private final NonNullAssumption nonNullAssumption;
private final Instruction origin;
- private Assume(An assumption, Value dest, Value src, Instruction origin, AppView<?> appView) {
+ private Assume(
+ DynamicTypeAssumption dynamicTypeAssumption,
+ NonNullAssumption nonNullAssumption,
+ Value dest,
+ Value src,
+ Instruction origin,
+ AppView<?> appView) {
super(dest, src);
- assert assumption != null;
- assert assumption.verifyCorrectnessOfValues(dest, src, appView);
+ assert dynamicTypeAssumption != null || nonNullAssumption != null;
+ assert BooleanUtils.intValue(dynamicTypeAssumption != null)
+ + BooleanUtils.intValue(nonNullAssumption != null)
+ == 1;
+ assert dynamicTypeAssumption == null
+ || dynamicTypeAssumption.verifyCorrectnessOfValues(dest, src, appView);
+ assert nonNullAssumption == null
+ || nonNullAssumption.verifyCorrectnessOfValues(dest, src, appView);
assert dest != null;
- this.assumption = assumption;
+ this.dynamicTypeAssumption = dynamicTypeAssumption;
+ this.nonNullAssumption = nonNullAssumption;
this.origin = origin;
}
- public static Assume<NoAssumption> createAssumeNoneInstruction(
+ public static Assume createAssumeNonNullInstruction(
Value dest, Value src, Instruction origin, AppView<?> appView) {
- return new Assume<>(NoAssumption.get(), dest, src, origin, appView);
+ return new Assume(null, NonNullAssumption.get(), dest, src, origin, appView);
}
- public static Assume<NonNullAssumption> createAssumeNonNullInstruction(
- Value dest, Value src, Instruction origin, AppView<?> appView) {
- return new Assume<>(NonNullAssumption.get(), dest, src, origin, appView);
- }
-
- public static Assume<DynamicTypeAssumption> createAssumeDynamicTypeInstruction(
+ public static Assume createAssumeDynamicTypeInstruction(
TypeElement dynamicUpperBoundType,
ClassTypeElement dynamicLowerBoundType,
Value dest,
Value src,
Instruction origin,
AppView<?> appView) {
- return new Assume<>(
+ return new Assume(
new DynamicTypeAssumption(dynamicUpperBoundType, dynamicLowerBoundType),
+ null,
dest,
src,
origin,
@@ -69,7 +79,7 @@
public boolean verifyInstructionIsNeeded(AppView<?> appView) {
if (isAssumeDynamicType()) {
- assert assumption.verifyCorrectnessOfValues(outValue(), src(), appView);
+ assert dynamicTypeAssumption.verifyCorrectnessOfValues(outValue(), src(), appView);
}
return true;
}
@@ -79,8 +89,12 @@
return visitor.visit(this);
}
- public An getAssumption() {
- return assumption;
+ public DynamicTypeAssumption getDynamicTypeAssumption() {
+ return dynamicTypeAssumption;
+ }
+
+ public NonNullAssumption getNonNullAssumption() {
+ return nonNullAssumption;
}
public Value src() {
@@ -98,9 +112,6 @@
@Override
public String getInstructionName() {
- if (isAssumeNone()) {
- return "AssumeNone";
- }
if (isAssumeDynamicType()) {
return "AssumeDynamicType";
}
@@ -116,51 +127,18 @@
}
@Override
- public Assume<An> asAssume() {
+ public Assume asAssume() {
return this;
}
@Override
- public boolean isAssumeNone() {
- return assumption.isAssumeNone();
- }
-
- @Override
- public Assume<NoAssumption> asAssumeNone() {
- assert isAssumeNone();
- @SuppressWarnings("unchecked")
- Assume<NoAssumption> self = (Assume<NoAssumption>) this;
- return self;
- }
-
- @Override
public boolean isAssumeDynamicType() {
- return assumption.isAssumeDynamicType();
- }
-
- @Override
- public Assume<DynamicTypeAssumption> asAssumeDynamicType() {
- assert isAssumeDynamicType();
- @SuppressWarnings("unchecked")
- Assume<DynamicTypeAssumption> self = (Assume<DynamicTypeAssumption>) this;
- return self;
+ return dynamicTypeAssumption != null;
}
@Override
public boolean isAssumeNonNull() {
- return assumption.isAssumeNonNull();
- }
-
- @Override
- public Assume<NonNullAssumption> asAssumeNonNull() {
- assert isAssumeNonNull();
- @SuppressWarnings("unchecked")
- Assume<NonNullAssumption> self = (Assume<NonNullAssumption>) this;
- return self;
- }
-
- public boolean mayAffectStaticType() {
- return isAssumeNonNull();
+ return nonNullAssumption != null;
}
@Override
@@ -171,12 +149,8 @@
if (outType.isPrimitiveType()) {
return false;
}
- if (assumption.isAssumeNone()) {
- // The main purpose of AssumeNone is to test local alias tracking.
- return true;
- }
- if (assumption.isAssumeDynamicType()) {
- outType = asAssumeDynamicType().assumption.getDynamicUpperBoundType();
+ if (isAssumeDynamicType()) {
+ outType = dynamicTypeAssumption.getDynamicUpperBoundType();
}
if (appView.appInfo().hasLiveness()) {
if (outType.isClassType()
@@ -233,8 +207,9 @@
if (!other.isAssume()) {
return false;
}
- Assume<?> assumeInstruction = other.asAssume();
- return assumption.equals(assumeInstruction.assumption);
+ Assume assumeInstruction = other.asAssume();
+ return Objects.equals(dynamicTypeAssumption, assumeInstruction.dynamicTypeAssumption)
+ && Objects.equals(nonNullAssumption, assumeInstruction.nonNullAssumption);
}
@Override
@@ -245,10 +220,10 @@
@Override
public TypeElement evaluate(AppView<?> appView) {
- if (assumption.isAssumeNone() || assumption.isAssumeDynamicType()) {
+ if (isAssumeDynamicType()) {
return src().getType();
}
- if (assumption.isAssumeNonNull()) {
+ if (isAssumeNonNull()) {
assert src().getType().isReferenceType();
return src().getType().asReferenceType().asMeetWithNotNull();
}
@@ -281,7 +256,7 @@
TypeElement inType = src().getType();
TypeElement outType = getOutType();
- if (isAssumeNone() || isAssumeDynamicType()) {
+ if (isAssumeDynamicType()) {
assert inType.isReferenceType() : inType;
assert outType.equals(inType)
: "At " + this + System.lineSeparator() + outType + " != " + inType;
@@ -303,63 +278,22 @@
// are still valid.
String originString =
origin.hasBlock() ? " (origin: `" + origin.toString() + "`)" : " (obsolete origin)";
- if (isAssumeNone() || isAssumeNonNull()) {
+ if (isAssumeNonNull()) {
return super.toString() + originString;
}
if (isAssumeDynamicType()) {
- DynamicTypeAssumption assumption = asAssumeDynamicType().getAssumption();
return super.toString()
+ "; upper bound: "
- + assumption.dynamicUpperBoundType
- + (assumption.dynamicLowerBoundType != null
- ? "; lower bound: " + assumption.dynamicLowerBoundType
+ + dynamicTypeAssumption.dynamicUpperBoundType
+ + (dynamicTypeAssumption.dynamicLowerBoundType != null
+ ? "; lower bound: " + dynamicTypeAssumption.dynamicLowerBoundType
: "")
+ originString;
}
return super.toString();
}
- abstract static class Assumption {
-
- public boolean isAssumeNone() {
- return false;
- }
-
- public boolean isAssumeDynamicType() {
- return false;
- }
-
- public boolean isAssumeNonNull() {
- return false;
- }
-
- public boolean verifyCorrectnessOfValues(Value dest, Value src, AppView<?> appView) {
- return true;
- }
- }
-
- public static class NoAssumption extends Assumption {
- private static final NoAssumption instance = new NoAssumption();
-
- private NoAssumption() {}
-
- static NoAssumption get() {
- return instance;
- }
-
- @Override
- public boolean isAssumeNone() {
- return true;
- }
-
- @Override
- public boolean verifyCorrectnessOfValues(Value dest, Value src, AppView<?> appView) {
- assert dest.getType() == src.getType();
- return true;
- }
- }
-
- public static class DynamicTypeAssumption extends Assumption {
+ public static class DynamicTypeAssumption {
private final TypeElement dynamicUpperBoundType;
private final ClassTypeElement dynamicLowerBoundType;
@@ -378,12 +312,6 @@
return dynamicLowerBoundType;
}
- @Override
- public boolean isAssumeDynamicType() {
- return true;
- }
-
- @Override
public boolean verifyCorrectnessOfValues(Value dest, Value src, AppView<?> appView) {
assert dynamicUpperBoundType.lessThanOrEqualUpToNullability(src.getType(), appView);
return true;
@@ -408,7 +336,7 @@
}
}
- public static class NonNullAssumption extends Assumption {
+ public static class NonNullAssumption {
private static final NonNullAssumption instance = new NonNullAssumption();
@@ -418,12 +346,6 @@
return instance;
}
- @Override
- public boolean isAssumeNonNull() {
- return true;
- }
-
- @Override
public boolean verifyCorrectnessOfValues(Value dest, Value src, AppView<?> appView) {
assert !src.isNeverNull();
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
index fdfbe61..f8793ab 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
@@ -35,7 +35,7 @@
}
@Override
- public T visit(Assume<?> instruction) {
+ public T visit(Assume instruction) {
return null;
}
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 4a6a082..a0af5e2 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
@@ -620,7 +620,7 @@
for (BasicBlock block : blocks) {
for (Instruction instruction : block.getInstructions()) {
if (instruction.isAssumeDynamicType()) {
- assert instruction.asAssumeDynamicType().verifyInstructionIsNeeded(appView);
+ assert instruction.asAssume().verifyInstructionIsNeeded(appView);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 115afec..b8a5787 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -128,6 +128,11 @@
DexEncodedField encodedField =
appInfoWithLiveness.resolveField(getField()).getResolvedField();
assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
+
+ if (encodedField.type().isAlwaysNull(appViewWithLiveness)) {
+ return false;
+ }
+
return appInfoWithLiveness.isFieldRead(encodedField)
|| isStoringObjectWithFinalizer(appViewWithLiveness, encodedField);
}
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 6d298df..1400455 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
@@ -22,9 +22,6 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
-import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
-import com.android.tools.r8.ir.code.Assume.NoAssumption;
-import com.android.tools.r8.ir.code.Assume.NonNullAssumption;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -716,15 +713,7 @@
return false;
}
- public Assume<?> asAssume() {
- return null;
- }
-
- public boolean isAssumeNone() {
- return false;
- }
-
- public Assume<NoAssumption> asAssumeNone() {
+ public Assume asAssume() {
return null;
}
@@ -732,18 +721,10 @@
return false;
}
- public Assume<DynamicTypeAssumption> asAssumeDynamicType() {
- return null;
- }
-
public boolean isAssumeNonNull() {
return false;
}
- public Assume<NonNullAssumption> asAssumeNonNull() {
- return null;
- }
-
public boolean isBinop() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
index 4bb29bb..a374439 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
@@ -24,7 +24,7 @@
T visit(ArrayPut instruction);
- T visit(Assume<?> instruction);
+ T visit(Assume instruction);
T visit(CheckCast instruction);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 8fad9af..1c19488 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -61,6 +61,7 @@
super(result, arguments);
}
+ @Deprecated
public static Invoke create(
Type type, DexItem target, DexProto proto, Value result, List<Value> arguments) {
return create(type, target, proto, result, arguments, false);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 1bc6ba6..0f736a0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -16,6 +16,8 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -27,15 +29,15 @@
public class InvokeDirect extends InvokeMethodWithReceiver {
- private final boolean itf;
+ private final boolean isInterface;
public InvokeDirect(DexMethod target, Value result, List<Value> arguments) {
this(target, result, arguments, false);
}
- public InvokeDirect(DexMethod target, Value result, List<Value> arguments, boolean itf) {
+ public InvokeDirect(DexMethod target, Value result, List<Value> arguments, boolean isInterface) {
super(target, result, arguments);
- this.itf = itf;
+ this.isInterface = isInterface;
// invoke-direct <init> should have no out value.
assert !target.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME)
|| result == null;
@@ -46,8 +48,9 @@
return Opcodes.INVOKE_DIRECT;
}
- public boolean isInterface() {
- return itf;
+ @Override
+ public boolean getInterfaceBit() {
+ return isInterface;
}
@Override
@@ -118,7 +121,10 @@
@Override
public DexEncodedMethod lookupSingleTarget(
- AppView<?> appView, ProgramMethod context, Value receiver) {
+ AppView<?> appView,
+ ProgramMethod context,
+ TypeElement receiverUpperBoundType,
+ ClassTypeElement receiverLowerBoundType) {
DexMethod invokedMethod = getInvokedMethod();
if (appView.appInfo().hasLiveness()) {
AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
@@ -140,7 +146,8 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), itf));
+ builder.add(
+ new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index f998009..e95be77 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.ir.analysis.type.TypeAnalysis.toRefinedReceiverType;
+
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeInterfaceRange;
import com.android.tools.r8.graph.AppView;
@@ -13,7 +15,8 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -28,6 +31,11 @@
}
@Override
+ public boolean getInterfaceBit() {
+ return true;
+ }
+
+ @Override
public int opcode() {
return Opcodes.INVOKE_INTERFACE;
}
@@ -88,7 +96,10 @@
@Override
public DexEncodedMethod lookupSingleTarget(
- AppView<?> appView, ProgramMethod context, Value receiver) {
+ AppView<?> appView,
+ ProgramMethod context,
+ TypeElement receiverUpperBoundType,
+ ClassTypeElement receiverLowerBoundType) {
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
return appViewWithLiveness
@@ -98,9 +109,9 @@
context,
true,
appView,
- TypeAnalysis.getRefinedReceiverType(
- appViewWithLiveness, getInvokedMethod(), receiver),
- receiver.getDynamicLowerBoundType(appViewWithLiveness));
+ toRefinedReceiverType(
+ receiverUpperBoundType, getInvokedMethod(), appViewWithLiveness),
+ receiverLowerBoundType);
}
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 8c059d9..c8dd6ec 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -41,6 +41,8 @@
this.method = target;
}
+ public abstract boolean getInterfaceBit();
+
@Override
public DexType getReturnType() {
return method.proto.returnType;
@@ -109,8 +111,7 @@
refinedReceiverLowerBound = null;
}
}
- ResolutionResult resolutionResult =
- appView.appInfo().resolveMethod(method, isInvokeInterface());
+ ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method, getInterfaceBit());
LookupResult lookupResult;
if (refinedReceiverUpperBound != null) {
lookupResult =
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index 496f869..a2aa502 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -73,15 +73,30 @@
@Override
public final DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
- return lookupSingleTarget(appView, context, getReceiver());
+ TypeElement receiverUpperBoundType = null;
+ ClassTypeElement receiverLowerBoundType = null;
+ if (appView.enableWholeProgramOptimizations()) {
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ receiverUpperBoundType = getReceiver().getDynamicUpperBoundType(appViewWithLiveness);
+ receiverLowerBoundType = getReceiver().getDynamicLowerBoundType(appViewWithLiveness);
+ }
+ return lookupSingleTarget(appView, context, receiverUpperBoundType, receiverLowerBoundType);
}
public abstract DexEncodedMethod lookupSingleTarget(
- AppView<?> appView, ProgramMethod context, Value receiver);
+ AppView<?> appView,
+ ProgramMethod context,
+ TypeElement receiverUpperBoundType,
+ ClassTypeElement receiverLowerBoundType);
public final ProgramMethod lookupSingleProgramTarget(
- AppView<?> appView, ProgramMethod context, Value receiver) {
- return asProgramMethodOrNull(lookupSingleTarget(appView, context, receiver), appView);
+ AppView<?> appView,
+ ProgramMethod context,
+ TypeElement receiverUpperBoundType,
+ ClassTypeElement receiverLowerBoundType) {
+ return asProgramMethodOrNull(
+ lookupSingleTarget(appView, context, receiverUpperBoundType, receiverLowerBoundType),
+ appView);
}
@Override
@@ -194,7 +209,7 @@
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
ResolutionResult resolutionResult =
- appViewWithLiveness.appInfo().resolveMethod(getInvokedMethod(), isInvokeInterface());
+ appViewWithLiveness.appInfo().resolveMethod(getInvokedMethod(), getInterfaceBit());
if (resolutionResult.isFailedResolution()) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 6a1e8a2..824a283 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -36,6 +36,11 @@
}
@Override
+ public boolean getInterfaceBit() {
+ return false;
+ }
+
+ @Override
public int opcode() {
return Opcodes.INVOKE_POLYMORPHIC;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 7cd9b1e..7f5836a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -43,6 +43,11 @@
}
@Override
+ public boolean getInterfaceBit() {
+ return isInterface;
+ }
+
+ @Override
public int opcode() {
return Opcodes.INVOKE_STATIC;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index e2b2e3a..4815431 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -13,6 +13,8 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -22,11 +24,16 @@
public class InvokeSuper extends InvokeMethodWithReceiver {
- public final boolean itf;
+ public final boolean isInterface;
- public InvokeSuper(DexMethod target, Value result, List<Value> arguments, boolean itf) {
+ public InvokeSuper(DexMethod target, Value result, List<Value> arguments, boolean isInterface) {
super(target, result, arguments);
- this.itf = itf;
+ this.isInterface = isInterface;
+ }
+
+ @Override
+ public boolean getInterfaceBit() {
+ return isInterface;
}
@Override
@@ -75,7 +82,8 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), itf));
+ builder.add(
+ new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface));
}
@Override
@@ -95,7 +103,10 @@
@Override
public DexEncodedMethod lookupSingleTarget(
- AppView<?> appView, ProgramMethod context, Value receiver) {
+ AppView<?> appView,
+ ProgramMethod context,
+ TypeElement receiverUpperBoundType,
+ ClassTypeElement receiverLowerBoundType) {
if (appView.appInfo().hasLiveness() && context != null) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 4739d83..2d95115 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.ir.analysis.type.TypeAnalysis.toRefinedReceiverType;
+
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeVirtualRange;
import com.android.tools.r8.graph.AppView;
@@ -14,7 +16,8 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -29,6 +32,11 @@
}
@Override
+ public boolean getInterfaceBit() {
+ return false;
+ }
+
+ @Override
public int opcode() {
return Opcodes.INVOKE_VIRTUAL;
}
@@ -89,12 +97,20 @@
@Override
public DexEncodedMethod lookupSingleTarget(
- AppView<?> appView, ProgramMethod context, Value receiver) {
- return lookupSingleTarget(appView, context, receiver, getInvokedMethod());
+ AppView<?> appView,
+ ProgramMethod context,
+ TypeElement receiverUpperBoundType,
+ ClassTypeElement receiverLowerBoundType) {
+ return lookupSingleTarget(
+ appView, context, receiverUpperBoundType, receiverLowerBoundType, getInvokedMethod());
}
public static DexEncodedMethod lookupSingleTarget(
- AppView<?> appView, ProgramMethod context, Value receiver, DexMethod method) {
+ AppView<?> appView,
+ ProgramMethod context,
+ TypeElement receiverUpperBoundType,
+ ClassTypeElement receiverLowerBoundType,
+ DexMethod method) {
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
return appViewWithLiveness
@@ -104,8 +120,8 @@
context,
false,
appView,
- TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, method, receiver),
- receiver.getDynamicLowerBoundType(appViewWithLiveness));
+ toRefinedReceiverType(receiverUpperBoundType, method, appViewWithLiveness),
+ receiverLowerBoundType);
}
// In D8, allow lookupSingleTarget() to be used for finding final library methods. This is used
// for library modeling.
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 678a569..36b6520 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -125,6 +125,10 @@
return false;
}
+ if (encodedField.type().isAlwaysNull(appViewWithLiveness)) {
+ return false;
+ }
+
return appInfoWithLiveness.isFieldRead(encodedField)
|| isStoringObjectWithFinalizer(appViewWithLiveness, encodedField);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index b79a063..53af0fc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -1173,16 +1173,15 @@
return root.getDynamicUpperBoundType(appView);
}
- // Try to find an alias of the receiver, which is defined by an instruction of the type
- // Assume<DynamicTypeAssumption>.
+ // Try to find an alias of the receiver, which is defined by an instruction of the type Assume.
Value aliasedValue =
getSpecificAliasedValue(value -> !value.isPhi() && value.definition.isAssumeDynamicType());
TypeElement lattice;
if (aliasedValue != null) {
- // If there is an alias of the receiver, which is defined by an Assume<DynamicTypeAssumption>
- // instruction, then use the dynamic type as the refined receiver type.
+ // If there is an alias of the receiver, which is defined by an Assume instruction that
+ // carries a dynamic type, then use the dynamic type as the refined receiver type.
lattice =
- aliasedValue.definition.asAssumeDynamicType().getAssumption().getDynamicUpperBoundType();
+ aliasedValue.definition.asAssume().getDynamicTypeAssumption().getDynamicUpperBoundType();
// For precision, verify that the dynamic type is at least as precise as the static type.
assert lattice.lessThanOrEqualUpToNullability(type, appView) : type + " < " + lattice;
@@ -1228,12 +1227,11 @@
return null;
}
- // Try to find an alias of the receiver, which is defined by an instruction of the type
- // Assume<DynamicTypeAssumption>.
+ // Try to find an alias of the receiver, which is defined by an instruction of the type Assume.
Value aliasedValue = getSpecificAliasedValue(value -> value.definition.isAssumeDynamicType());
if (aliasedValue != null) {
ClassTypeElement lattice =
- aliasedValue.definition.asAssumeDynamicType().getAssumption().getDynamicLowerBoundType();
+ aliasedValue.definition.asAssume().getDynamicTypeAssumption().getDynamicLowerBoundType();
return lattice != null && type.isDefinitelyNotNull() && lattice.isNullable()
? lattice.asMeetWithNotNull()
: lattice;
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 dba65f7..844ccce 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
@@ -52,6 +52,7 @@
import com.android.tools.r8.ir.desugar.StringConcatRewriter;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
+import com.android.tools.r8.ir.optimize.AssumeInserter;
import com.android.tools.r8.ir.optimize.Assumer;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
@@ -64,7 +65,6 @@
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.MemberValuePropagation;
-import com.android.tools.r8.ir.optimize.NonNullTracker;
import com.android.tools.r8.ir.optimize.Outliner;
import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
import com.android.tools.r8.ir.optimize.RedundantFieldLoadElimination;
@@ -292,7 +292,7 @@
assert appView.rootSet() != null;
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
if (options.enableNonNullTracking) {
- assumers.add(new NonNullTracker(appViewWithLiveness));
+ assumers.add(new AssumeInserter(appViewWithLiveness));
}
this.classInliner =
options.enableClassInlining && options.enableInlining ? new ClassInliner() : null;
@@ -797,8 +797,10 @@
if (outliner != null) {
printPhase("Outlining");
timing.begin("IR conversion phase 3");
- if (outliner.selectMethodsForOutlining()) {
+ ProgramMethodSet methodsSelectedForOutlining = outliner.selectMethodsForOutlining();
+ if (!methodsSelectedForOutlining.isEmpty()) {
forEachSelectedOutliningMethod(
+ methodsSelectedForOutlining,
code -> {
printMethod(code, "IR before outlining (SSA)", null);
outliner.identifyOutlineSites(code);
@@ -808,6 +810,7 @@
appView.appInfo().addSynthesizedClass(outlineClass);
optimizeSynthesizedClass(outlineClass, executorService);
forEachSelectedOutliningMethod(
+ methodsSelectedForOutlining,
code -> {
outliner.applyOutliningCandidate(code);
printMethod(code, "IR after outlining (SSA)", null);
@@ -839,9 +842,6 @@
if (libraryMethodOverrideAnalysis != null) {
libraryMethodOverrideAnalysis.logResults();
}
- if (uninstantiatedTypeOptimization != null) {
- uninstantiatedTypeOptimization.logResults();
- }
if (stringOptimizer != null) {
stringOptimizer.logResult();
}
@@ -898,11 +898,13 @@
}
private void forEachSelectedOutliningMethod(
- Consumer<IRCode> consumer, ExecutorService executorService)
+ ProgramMethodSet methodsSelectedForOutlining,
+ Consumer<IRCode> consumer,
+ ExecutorService executorService)
throws ExecutionException {
assert !options.skipIR;
ThreadUtils.processItems(
- outliner.buildMethodsSelectedForOutlining(),
+ methodsSelectedForOutlining,
method -> {
IRCode code = method.buildIR(appView);
assert code != null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index e94d07e..f83952c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -29,6 +29,7 @@
import static com.android.tools.r8.ir.code.Opcodes.RETURN;
import static com.android.tools.r8.ir.code.Opcodes.STATIC_GET;
import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
+import static com.android.tools.r8.utils.ObjectUtils.getBooleanOrElse;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -293,8 +294,18 @@
assert newInValues.size()
== actualTarget.proto.parameters.size() + (actualInvokeType == STATIC ? 0 : 1);
+ // TODO(b/157111832): This bit should be part of the graph lens lookup result.
+ boolean isInterface =
+ getBooleanOrElse(
+ appView.definitionFor(actualTarget.holder), DexClass::isInterface, false);
Invoke newInvoke =
- Invoke.create(actualInvokeType, actualTarget, null, newOutValue, newInValues);
+ Invoke.create(
+ actualInvokeType,
+ actualTarget,
+ null,
+ newOutValue,
+ newInValues,
+ isInterface);
iterator.replaceCurrentInstruction(newInvoke);
if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
affectedPhis.addAll(newOutValue.uniquePhiUsers());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 250737f..53ff67e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -114,7 +114,7 @@
ResolutionResult resolutionResult =
appView
.appInfoForDesugaring()
- .resolveMethod(invoke.getInvokedMethod(), invoke.isInvokeInterface());
+ .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit());
if (resolutionResult.isFailedResolution()) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index f41bdce..d203a12 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -540,16 +540,6 @@
boolean holderIsInterface() {
InternalOptions options = appView.options();
- if (!options.isGeneratingClassFiles()) {
- // When generating dex the value of this flag on invokes does not matter (unused).
- // We cannot know if definitionFor(implMethod.holder) is null or not in that case,
- // so we cannot set the flag and just return false.
- return false;
- }
- // The only case where we do Lambda desugaring with Cf to Cf is in L8.
- // If the compilation is not coreLibraryCompilation, then the assertion
- // implMethodHolder != null may fail, hence the assertion.
- assert options.cfToCfDesugar;
DexMethod implMethod = descriptor.implHandle.asMethod();
DexClass implMethodHolder = appView.definitionFor(implMethod.holder);
if (implMethodHolder == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
index 9102f53..7f45db3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.Assume;
-import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -34,8 +33,7 @@
private final IRCode code;
private final Set<Value> affectedValues = Sets.newIdentityHashSet();
- private final Set<Assume<DynamicTypeAssumption>> assumeDynamicTypeInstructionsToRemove =
- Sets.newIdentityHashSet();
+ private final Set<Assume> assumeDynamicTypeInstructionsToRemove = Sets.newIdentityHashSet();
private boolean mayHaveIntroducedTrivialPhi = false;
@@ -48,21 +46,20 @@
return mayHaveIntroducedTrivialPhi;
}
- public void markForRemoval(Assume<DynamicTypeAssumption> assumeDynamicTypeInstruction) {
+ public void markForRemoval(Assume assumeDynamicTypeInstruction) {
assumeDynamicTypeInstructionsToRemove.add(assumeDynamicTypeInstruction);
}
public void markUsersForRemoval(Value value) {
for (Instruction user : value.aliasedUsers()) {
if (user.isAssumeDynamicType()) {
- markForRemoval(user.asAssumeDynamicType());
+ markForRemoval(user.asAssume());
}
}
}
public void removeIfMarked(
- Assume<DynamicTypeAssumption> assumeDynamicTypeInstruction,
- InstructionListIterator instructionIterator) {
+ Assume assumeDynamicTypeInstruction, InstructionListIterator instructionIterator) {
if (assumeDynamicTypeInstructionsToRemove.remove(assumeDynamicTypeInstruction)) {
Value inValue = assumeDynamicTypeInstruction.src();
Value outValue = assumeDynamicTypeInstruction.outValue();
@@ -91,7 +88,7 @@
while (instructionIterator.hasNext()) {
Instruction instruction = instructionIterator.next();
if (instruction.isAssumeDynamicType()) {
- removeIfMarked(instruction.asAssumeDynamicType(), instructionIterator);
+ removeIfMarked(instruction.asAssume(), instructionIterator);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
similarity index 66%
rename from src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
rename to src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index e08aeae..611a0a1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -48,11 +48,11 @@
import java.util.function.BiPredicate;
import java.util.function.Predicate;
-public class NonNullTracker implements Assumer {
+public class AssumeInserter implements Assumer {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- public NonNullTracker(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public AssumeInserter(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
@@ -67,7 +67,7 @@
BasicBlockIterator blockIterator,
Predicate<BasicBlock> blockTester,
Timing timing) {
- timing.begin("Insert assume not null instructions");
+ timing.begin("Insert assume instructions");
internalInsertAssumeInstructionsInBlocks(code, blockIterator, blockTester, timing);
timing.end();
}
@@ -77,54 +77,54 @@
BasicBlockIterator blockIterator,
Predicate<BasicBlock> blockTester,
Timing timing) {
- timing.begin("Part 1: Compute non null values");
- NonNullValues nonNullValues = computeNonNullValues(code, blockIterator, blockTester);
+ timing.begin("Part 1: Compute assumed values");
+ AssumedValues assumedValues = computeAssumedValues(code, blockIterator, blockTester);
timing.end();
- if (nonNullValues.isEmpty()) {
+ if (assumedValues.isEmpty()) {
return;
}
timing.begin("Part 2: Remove redundant assume instructions");
- removeRedundantAssumeInstructions(nonNullValues);
+ removeRedundantAssumeInstructions(assumedValues);
timing.end();
timing.begin("Part 3: Compute dominated users");
Map<Instruction, Set<Value>> redundantKeys =
- computeDominanceForNonNullValues(code, nonNullValues);
+ computeDominanceForAssumedValues(code, assumedValues);
timing.end();
- if (nonNullValues.isEmpty()) {
+ if (assumedValues.isEmpty()) {
return;
}
timing.begin("Part 4: Remove redundant dominated assume instructions");
- removeRedundantDominatedAssumeInstructions(nonNullValues, redundantKeys);
+ removeRedundantDominatedAssumeInstructions(assumedValues, redundantKeys);
timing.end();
- if (nonNullValues.isEmpty()) {
+ if (assumedValues.isEmpty()) {
return;
}
timing.begin("Part 5: Materialize assume instructions");
- materializeAssumeInstructions(code, nonNullValues);
+ materializeAssumeInstructions(code, assumedValues);
timing.end();
}
- private NonNullValues computeNonNullValues(
+ private AssumedValues computeAssumedValues(
IRCode code, BasicBlockIterator blockIterator, Predicate<BasicBlock> blockTester) {
- NonNullValues.Builder nonNullValuesBuilder = new NonNullValues.Builder();
+ AssumedValues.Builder assumedValuesBuilder = new AssumedValues.Builder();
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
if (blockTester.test(block)) {
- computeNonNullValuesInBlock(code, blockIterator, block, nonNullValuesBuilder);
+ computeAssumedValuesInBlock(code, blockIterator, block, assumedValuesBuilder);
}
}
- return nonNullValuesBuilder.build();
+ return assumedValuesBuilder.build();
}
- private void computeNonNullValuesInBlock(
+ private void computeAssumedValuesInBlock(
IRCode code,
BasicBlockIterator blockIterator,
BasicBlock block,
- NonNullValues.Builder nonNullValuesBuilder) {
+ AssumedValues.Builder assumedValuesBuilder) {
// Add non-null after
// 1) instructions that implicitly indicate receiver/array is not null.
// 2) invocations that are guaranteed to return a non-null value.
@@ -138,9 +138,9 @@
// Case (1), instructions that implicitly indicate receiver/array is not null.
if (current.throwsOnNullInput()) {
Value inValue = current.getNonNullInput();
- if (nonNullValuesBuilder.isMaybeNull(inValue)
+ if (assumedValuesBuilder.isMaybeNull(inValue)
&& isNullableReferenceTypeWithOtherNonDebugUsers(inValue, current)) {
- nonNullValuesBuilder.addNonNullValueWithUnknownDominance(current, inValue);
+ assumedValuesBuilder.addNonNullValueWithUnknownDominance(current, inValue);
needsAssumeInstruction = true;
}
}
@@ -151,7 +151,7 @@
if (invoke.hasOutValue() || !invoke.getInvokedMethod().proto.parameters.isEmpty()) {
// Case (2) and (3).
needsAssumeInstruction |=
- computeNonNullValuesFromSingleTarget(code, invoke, nonNullValuesBuilder);
+ computeAssumedValuesFromSingleTarget(code, invoke, assumedValuesBuilder);
}
} else if (current.isFieldGet()) {
// Case (4), field-get instructions that are guaranteed to read a non-null value.
@@ -163,7 +163,7 @@
FieldOptimizationInfo optimizationInfo = encodedField.getOptimizationInfo();
if (optimizationInfo.getDynamicUpperBoundType() != null
&& optimizationInfo.getDynamicUpperBoundType().isDefinitelyNotNull()) {
- nonNullValuesBuilder.addNonNullValueKnownToDominateAllUsers(current, outValue);
+ assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(current, outValue);
needsAssumeInstruction = true;
}
}
@@ -182,7 +182,7 @@
assert !instructionIterator.hasNext();
assert instructionIterator.peekPrevious().isGoto();
assert blockIterator.peekPrevious() == insertionBlock;
- computeNonNullValuesInBlock(code, blockIterator, insertionBlock, nonNullValuesBuilder);
+ computeAssumedValuesInBlock(code, blockIterator, insertionBlock, assumedValuesBuilder);
return;
}
if (current.instructionTypeCanThrow()) {
@@ -194,16 +194,16 @@
If ifInstruction = block.exit().asIf();
if (ifInstruction != null && ifInstruction.isNonTrivialNullTest()) {
Value lhs = ifInstruction.lhs();
- if (nonNullValuesBuilder.isMaybeNull(lhs)
+ if (assumedValuesBuilder.isMaybeNull(lhs)
&& isNullableReferenceTypeWithOtherNonDebugUsers(lhs, ifInstruction)
&& ifInstruction.targetFromNonNullObject().getPredecessors().size() == 1) {
- nonNullValuesBuilder.addNonNullValueWithUnknownDominance(ifInstruction, lhs);
+ assumedValuesBuilder.addNonNullValueWithUnknownDominance(ifInstruction, lhs);
}
}
}
- private boolean computeNonNullValuesFromSingleTarget(
- IRCode code, InvokeMethod invoke, NonNullValues.Builder nonNullValuesBuilder) {
+ private boolean computeAssumedValuesFromSingleTarget(
+ IRCode code, InvokeMethod invoke, AssumedValues.Builder assumedValuesBuilder) {
DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
if (singleTarget == null) {
return false;
@@ -217,7 +217,7 @@
if (outValue != null
&& optimizationInfo.neverReturnsNull()
&& isNullableReferenceTypeWithNonDebugUsers(outValue)) {
- nonNullValuesBuilder.addNonNullValueKnownToDominateAllUsers(invoke, outValue);
+ assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(invoke, outValue);
needsAssumeInstruction = true;
}
@@ -228,9 +228,9 @@
for (int i = start; i < invoke.arguments().size(); i++) {
if (nonNullParamOnNormalExits.get(i)) {
Value argument = invoke.getArgument(i);
- if (nonNullValuesBuilder.isMaybeNull(argument)
+ if (assumedValuesBuilder.isMaybeNull(argument)
&& isNullableReferenceTypeWithOtherNonDebugUsers(argument, invoke)) {
- nonNullValuesBuilder.addNonNullValueWithUnknownDominance(invoke, argument);
+ assumedValuesBuilder.addNonNullValueWithUnknownDominance(invoke, argument);
needsAssumeInstruction = true;
}
}
@@ -239,33 +239,33 @@
return needsAssumeInstruction;
}
- private void removeRedundantAssumeInstructions(NonNullValues nonNullValues) {
- nonNullValues.removeIf(
- (instruction, nonNullValue) -> {
- if (nonNullValue.isPhi()) {
+ private void removeRedundantAssumeInstructions(AssumedValues assumedValues) {
+ assumedValues.removeIf(
+ (instruction, assumedValue) -> {
+ if (assumedValue.isPhi()) {
return false;
}
- Instruction definition = nonNullValue.definition;
- return definition != instruction && nonNullValues.contains(definition, nonNullValue);
+ Instruction definition = assumedValue.definition;
+ return definition != instruction && assumedValues.contains(definition, assumedValue);
});
}
- private Map<Instruction, Set<Value>> computeDominanceForNonNullValues(
- IRCode code, NonNullValues nonNullValues) {
+ private Map<Instruction, Set<Value>> computeDominanceForAssumedValues(
+ IRCode code, AssumedValues assumedValues) {
Map<Instruction, Set<Value>> redundantKeys = new IdentityHashMap<>();
LazyDominatorTree lazyDominatorTree = new LazyDominatorTree(code);
Map<BasicBlock, Set<BasicBlock>> dominatedBlocksCache = new IdentityHashMap<>();
- nonNullValues.computeDominance(
- (instruction, nonNullValue) -> {
- Set<Value> alreadyNonNullValues = redundantKeys.get(instruction);
- if (alreadyNonNullValues != null && alreadyNonNullValues.contains(nonNullValue)) {
- // Returning redundant() will cause the entry (instruction, nonNullValue) to be removed.
- return NonNullDominance.redundant();
+ assumedValues.computeDominance(
+ (instruction, assumedValue) -> {
+ Set<Value> alreadyAssumedValues = redundantKeys.get(instruction);
+ if (alreadyAssumedValues != null && alreadyAssumedValues.contains(assumedValue)) {
+ // Returning redundant() will cause the entry (instruction, assumedValue) to be removed.
+ return AssumedDominance.redundant();
}
// If this value is non-null since its definition, then it is known to dominate all users.
- if (nonNullValue == instruction.outValue()) {
- return NonNullDominance.everything();
+ if (assumedValue == instruction.outValue()) {
+ return AssumedDominance.everything();
}
// If we learn that this value is known to be non-null in the same block as it is defined,
@@ -273,12 +273,12 @@
// check, then the non-null-value is known to dominate all other users than the null check
// itself.
BasicBlock block = instruction.getBlock();
- if (nonNullValue.getBlock() == block
+ if (assumedValue.getBlock() == block
&& block.exit().isGoto()
&& !instruction.getBlock().hasCatchHandlers()) {
InstructionIterator iterator = instruction.getBlock().iterator();
- if (!nonNullValue.isPhi()) {
- iterator.nextUntil(x -> x != nonNullValue.definition);
+ if (!assumedValue.isPhi()) {
+ iterator.nextUntil(x -> x != assumedValue.definition);
iterator.previous();
}
boolean isUsedBeforeInstruction = false;
@@ -287,23 +287,23 @@
if (current == instruction) {
break;
}
- if (current.inValues().contains(nonNullValue)
- || current.getDebugValues().contains(nonNullValue)) {
+ if (current.inValues().contains(assumedValue)
+ || current.getDebugValues().contains(assumedValue)) {
isUsedBeforeInstruction = true;
break;
}
}
if (!isUsedBeforeInstruction) {
- return NonNullDominance.everythingElse();
+ return AssumedDominance.everythingElse();
}
}
// Otherwise, we need a dominator tree to determine which users are dominated.
BasicBlock insertionBlock = getInsertionBlock(instruction);
- assert nonNullValue.hasPhiUsers()
- || nonNullValue.uniqueUsers().stream().anyMatch(user -> user != instruction)
- || nonNullValue.isArgument();
+ assert assumedValue.hasPhiUsers()
+ || assumedValue.uniqueUsers().stream().anyMatch(user -> user != instruction)
+ || assumedValue.isArgument();
// Find all users of the original value that are dominated by either the current block
// or the new split-off block. Since NPE can be explicitly caught, nullness should be
@@ -313,8 +313,8 @@
dominatedBlocksCache.computeIfAbsent(
insertionBlock, x -> dominatorTree.dominatedBlocks(x, Sets.newIdentityHashSet()));
- NonNullDominance.Builder dominance = NonNullDominance.builder(nonNullValue);
- for (Instruction user : nonNullValue.uniqueUsers()) {
+ AssumedDominance.Builder dominance = AssumedDominance.builder(assumedValue);
+ for (Instruction user : assumedValue.uniqueUsers()) {
if (user != instruction && dominatedBlocks.contains(user.getBlock())) {
if (user.getBlock() == insertionBlock && insertionBlock == block) {
Instruction first = block.iterator().nextUntil(x -> x == instruction || x == user);
@@ -329,12 +329,12 @@
// after the given user in case the user is also a null check for the non-null-value.
redundantKeys
.computeIfAbsent(user, ignore -> Sets.newIdentityHashSet())
- .add(nonNullValue);
+ .add(assumedValue);
}
}
- for (Phi user : nonNullValue.uniquePhiUsers()) {
+ for (Phi user : assumedValue.uniquePhiUsers()) {
IntList dominatedPredecessorIndices =
- findDominatedPredecessorIndexesInPhi(user, nonNullValue, dominatedBlocks);
+ findDominatedPredecessorIndexesInPhi(user, assumedValue, dominatedBlocks);
if (!dominatedPredecessorIndices.isEmpty()) {
dominance.addDominatedPhiUser(user, dominatedPredecessorIndices);
}
@@ -345,30 +345,31 @@
}
private void removeRedundantDominatedAssumeInstructions(
- NonNullValues nonNullValues, Map<Instruction, Set<Value>> redundantKeys) {
- nonNullValues.removeAll(redundantKeys);
+ AssumedValues assumedValues, Map<Instruction, Set<Value>> redundantKeys) {
+ assumedValues.removeAll(redundantKeys);
}
- private void materializeAssumeInstructions(IRCode code, NonNullValues nonNullValues) {
+ private void materializeAssumeInstructions(IRCode code, AssumedValues assumedValues) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
Map<BasicBlock, Map<Instruction, List<Instruction>>> pendingInsertions =
new IdentityHashMap<>();
- nonNullValues.forEach(
- (instruction, nonNullValue, dominance) -> {
+ assumedValues.forEach(
+ (instruction, assumedValue, assumedValueInfo) -> {
BasicBlock block = instruction.getBlock();
BasicBlock insertionBlock = getInsertionBlock(instruction);
+ AssumedDominance dominance = assumedValueInfo.getDominance();
Value newValue =
code.createValue(
- nonNullValue.getType().asReferenceType().asMeetWithNotNull(),
- nonNullValue.getLocalInfo());
+ assumedValue.getType().asReferenceType().asMeetWithNotNull(),
+ assumedValue.getLocalInfo());
if (dominance.isEverything()) {
- nonNullValue.replaceUsers(newValue);
+ assumedValue.replaceUsers(newValue);
} else if (dominance.isEverythingElse()) {
- nonNullValue.replaceSelectiveInstructionUsers(newValue, user -> user != instruction);
- nonNullValue.replacePhiUsers(newValue);
+ assumedValue.replaceSelectiveInstructionUsers(newValue, user -> user != instruction);
+ assumedValue.replacePhiUsers(newValue);
} else if (dominance.isSomething()) {
- SomethingNonNullDominance somethingDominance = dominance.asSomething();
+ SomethingAssumedDominance somethingDominance = dominance.asSomething();
somethingDominance
.getDominatedPhiUsers()
.forEach(
@@ -376,22 +377,22 @@
IntListIterator iterator = indices.iterator();
while (iterator.hasNext()) {
Value operand = user.getOperand(iterator.nextInt());
- if (operand != nonNullValue) {
+ if (operand != assumedValue) {
assert operand.isDefinedByInstructionSatisfying(
Instruction::isAssumeNonNull);
iterator.remove();
}
}
});
- nonNullValue.replaceSelectiveUsers(
+ assumedValue.replaceSelectiveUsers(
newValue,
somethingDominance.getDominatedUsers(),
somethingDominance.getDominatedPhiUsers());
}
affectedValues.addAll(newValue.affectedValues());
- Assume<NonNullAssumption> assumeInstruction =
- Assume.createAssumeNonNullInstruction(newValue, nonNullValue, instruction, appView);
+ Assume assumeInstruction =
+ Assume.createAssumeNonNullInstruction(newValue, assumedValue, instruction, appView);
assumeInstruction.setPosition(instruction.getPosition());
if (insertionBlock != block) {
insertionBlock.listIterator(code).add(assumeInstruction);
@@ -431,8 +432,8 @@
}
private IntList findDominatedPredecessorIndexesInPhi(
- Phi user, Value knownToBeNonNullValue, Set<BasicBlock> dominatedBlocks) {
- assert user.getOperands().contains(knownToBeNonNullValue);
+ Phi user, Value assumedValue, Set<BasicBlock> dominatedBlocks) {
+ assert user.getOperands().contains(assumedValue);
List<Value> operands = user.getOperands();
List<BasicBlock> predecessors = user.getBlock().getPredecessors();
assert operands.size() == predecessors.size();
@@ -446,7 +447,7 @@
BasicBlock predecessor = predecessorIterator.next();
// When this phi is chosen to be known-to-be-non-null value,
// check if the corresponding predecessor is dominated by the block where non-null is added.
- if (operand == knownToBeNonNullValue && dominatedBlocks.contains(predecessor)) {
+ if (operand == assumedValue && dominatedBlocks.contains(predecessor)) {
predecessorIndexes.add(index);
}
@@ -479,47 +480,70 @@
return false;
}
- static class NonNullValues {
+ static class AssumedValueInfo {
+
+ AssumedDominance dominance;
+ NonNullAssumption nonNullAssumption;
+
+ AssumedValueInfo(AssumedDominance dominance) {
+ this.dominance = dominance;
+ }
+
+ AssumedDominance getDominance() {
+ return dominance;
+ }
+
+ void setDominance(AssumedDominance dominance) {
+ this.dominance = dominance;
+ }
+
+ void setNotNull() {
+ nonNullAssumption = NonNullAssumption.get();
+ }
+ }
+
+ static class AssumedValues {
/**
* A mapping from each instruction to the (in and out) values that are guaranteed to be non-null
* by the instruction. Each non-null value is subsequently mapped to the set of users that it
* dominates.
*/
- Map<Instruction, Map<Value, NonNullDominance>> nonNullValues;
+ Map<Instruction, Map<Value, AssumedValueInfo>> assumedValues;
- public NonNullValues(Map<Instruction, Map<Value, NonNullDominance>> nonNullValues) {
- this.nonNullValues = nonNullValues;
+ public AssumedValues(Map<Instruction, Map<Value, AssumedValueInfo>> assumedValues) {
+ this.assumedValues = assumedValues;
}
public static Builder builder() {
return new Builder();
}
- void computeDominance(BiFunction<Instruction, Value, NonNullDominance> function) {
- Iterator<Entry<Instruction, Map<Value, NonNullDominance>>> outerIterator =
- nonNullValues.entrySet().iterator();
+ void computeDominance(BiFunction<Instruction, Value, AssumedDominance> function) {
+ Iterator<Entry<Instruction, Map<Value, AssumedValueInfo>>> outerIterator =
+ assumedValues.entrySet().iterator();
while (outerIterator.hasNext()) {
- Entry<Instruction, Map<Value, NonNullDominance>> outerEntry = outerIterator.next();
+ Entry<Instruction, Map<Value, AssumedValueInfo>> outerEntry = outerIterator.next();
Instruction instruction = outerEntry.getKey();
- Map<Value, NonNullDominance> dominancePerValue = outerEntry.getValue();
- Iterator<Entry<Value, NonNullDominance>> innerIterator =
+ Map<Value, AssumedValueInfo> dominancePerValue = outerEntry.getValue();
+ Iterator<Entry<Value, AssumedValueInfo>> innerIterator =
dominancePerValue.entrySet().iterator();
while (innerIterator.hasNext()) {
- Entry<Value, NonNullDominance> innerEntry = innerIterator.next();
- Value nonNullValue = innerEntry.getKey();
- NonNullDominance dominance = innerEntry.getValue();
+ Entry<Value, AssumedValueInfo> innerEntry = innerIterator.next();
+ Value assumedValue = innerEntry.getKey();
+ AssumedValueInfo assumedValueInfo = innerEntry.getValue();
+ AssumedDominance dominance = assumedValueInfo.dominance;
if (dominance.isEverything()) {
- assert nonNullValue.isDefinedByInstructionSatisfying(
- definition -> definition.outValue() == nonNullValue);
+ assert assumedValue.isDefinedByInstructionSatisfying(
+ definition -> definition.outValue() == assumedValue);
continue;
}
assert dominance.isUnknown();
- dominance = function.apply(instruction, nonNullValue);
- if ((dominance.isNothing() && !nonNullValue.isArgument()) || dominance.isUnknown()) {
+ dominance = function.apply(instruction, assumedValue);
+ if ((dominance.isNothing() && !assumedValue.isArgument()) || dominance.isUnknown()) {
innerIterator.remove();
} else {
- innerEntry.setValue(dominance);
+ assumedValueInfo.setDominance(dominance);
}
}
if (dominancePerValue.isEmpty()) {
@@ -528,48 +552,48 @@
}
}
- boolean contains(Instruction instruction, Value nonNullValue) {
- Map<Value, NonNullDominance> dominancePerValue = nonNullValues.get(instruction);
- return dominancePerValue != null && dominancePerValue.containsKey(nonNullValue);
+ boolean contains(Instruction instruction, Value assumedValue) {
+ Map<Value, AssumedValueInfo> dominancePerValue = assumedValues.get(instruction);
+ return dominancePerValue != null && dominancePerValue.containsKey(assumedValue);
}
boolean isEmpty() {
- return nonNullValues.isEmpty();
+ return assumedValues.isEmpty();
}
- void forEach(TriConsumer<Instruction, Value, NonNullDominance> consumer) {
- nonNullValues.forEach(
+ void forEach(TriConsumer<Instruction, Value, AssumedValueInfo> consumer) {
+ assumedValues.forEach(
(instruction, dominancePerValue) ->
dominancePerValue.forEach(
- (nonNullValue, dominance) ->
- consumer.accept(instruction, nonNullValue, dominance)));
+ (assumedValue, assumedValueInfo) ->
+ consumer.accept(instruction, assumedValue, assumedValueInfo)));
}
void removeAll(Map<Instruction, Set<Value>> keys) {
keys.forEach(
(instruction, values) -> {
- Map<Value, NonNullDominance> dominancePerValue = nonNullValues.get(instruction);
+ Map<Value, AssumedValueInfo> dominancePerValue = assumedValues.get(instruction);
if (dominancePerValue != null) {
values.forEach(dominancePerValue::remove);
if (dominancePerValue.isEmpty()) {
- nonNullValues.remove(instruction);
+ assumedValues.remove(instruction);
}
}
});
}
void removeIf(BiPredicate<Instruction, Value> predicate) {
- Iterator<Entry<Instruction, Map<Value, NonNullDominance>>> outerIterator =
- nonNullValues.entrySet().iterator();
+ Iterator<Entry<Instruction, Map<Value, AssumedValueInfo>>> outerIterator =
+ assumedValues.entrySet().iterator();
while (outerIterator.hasNext()) {
- Entry<Instruction, Map<Value, NonNullDominance>> outerEntry = outerIterator.next();
+ Entry<Instruction, Map<Value, AssumedValueInfo>> outerEntry = outerIterator.next();
Instruction instruction = outerEntry.getKey();
- Map<Value, NonNullDominance> dominancePerValue = outerEntry.getValue();
- Iterator<Entry<Value, NonNullDominance>> innerIterator =
+ Map<Value, AssumedValueInfo> dominancePerValue = outerEntry.getValue();
+ Iterator<Entry<Value, AssumedValueInfo>> innerIterator =
dominancePerValue.entrySet().iterator();
while (innerIterator.hasNext()) {
- Value nonNullValue = innerIterator.next().getKey();
- if (predicate.test(instruction, nonNullValue)) {
+ Value assumedValue = innerIterator.next().getKey();
+ if (predicate.test(instruction, assumedValue)) {
innerIterator.remove();
}
}
@@ -581,40 +605,42 @@
static class Builder {
- private final Map<Instruction, Map<Value, NonNullDominance>> nonNullValues =
+ private final Map<Instruction, Map<Value, AssumedValueInfo>> assumedValues =
new LinkedHashMap<>();
// Used to avoid unnecessary block splitting during phase 1.
- private final Set<Value> nonNullValuesKnownToDominateAllUsers = Sets.newIdentityHashSet();
+ private final Set<Value> assumedValuesKnownToDominateAllUsers = Sets.newIdentityHashSet();
- private void add(Instruction instruction, Value nonNullValue, NonNullDominance dominance) {
- nonNullValues
+ private void addNonNullValue(
+ Instruction instruction, Value nonNullValue, AssumedDominance dominance) {
+ assumedValues
.computeIfAbsent(instruction, ignore -> new LinkedHashMap<>())
- .put(nonNullValue, dominance);
+ .computeIfAbsent(nonNullValue, ignore -> new AssumedValueInfo(dominance))
+ .setNotNull();
if (dominance.isEverything()) {
- nonNullValuesKnownToDominateAllUsers.add(nonNullValue);
+ assumedValuesKnownToDominateAllUsers.add(nonNullValue);
}
}
void addNonNullValueKnownToDominateAllUsers(Instruction instruction, Value nonNullValue) {
- add(instruction, nonNullValue, NonNullDominance.everything());
+ addNonNullValue(instruction, nonNullValue, AssumedDominance.everything());
}
void addNonNullValueWithUnknownDominance(Instruction instruction, Value nonNullValue) {
- add(instruction, nonNullValue, NonNullDominance.unknown());
+ addNonNullValue(instruction, nonNullValue, AssumedDominance.unknown());
}
public boolean isMaybeNull(Value value) {
- return !nonNullValuesKnownToDominateAllUsers.contains(value);
+ return !assumedValuesKnownToDominateAllUsers.contains(value);
}
- public NonNullValues build() {
- return new NonNullValues(nonNullValues);
+ public AssumedValues build() {
+ return new AssumedValues(assumedValues);
}
}
}
- abstract static class NonNullDominance {
+ abstract static class AssumedDominance {
boolean isEverything() {
return false;
@@ -632,7 +658,7 @@
return false;
}
- SomethingNonNullDominance asSomething() {
+ SomethingAssumedDominance asSomething() {
return null;
}
@@ -640,76 +666,76 @@
return false;
}
- public static Builder builder(Value nonNullValue) {
- return new Builder(nonNullValue);
+ public static Builder builder(Value assumedValue) {
+ return new Builder(assumedValue);
}
- public static EverythingNonNullDominance everything() {
- return EverythingNonNullDominance.getInstance();
+ public static EverythingAssumedDominance everything() {
+ return EverythingAssumedDominance.getInstance();
}
- public static EverythingElseNonNullDominance everythingElse() {
- return EverythingElseNonNullDominance.getInstance();
+ public static EverythingElseAssumedDominance everythingElse() {
+ return EverythingElseAssumedDominance.getInstance();
}
- public static NothingNonNullDominance nothing() {
- return NothingNonNullDominance.getInstance();
+ public static NothingAssumedDominance nothing() {
+ return NothingAssumedDominance.getInstance();
}
- public static UnknownNonNullDominance redundant() {
+ public static UnknownAssumedDominance redundant() {
return unknown();
}
- public static SomethingNonNullDominance something(
+ public static SomethingAssumedDominance something(
Set<Instruction> dominatedUsers, Map<Phi, IntList> dominatedPhiUsers) {
- return new SomethingNonNullDominance(dominatedUsers, dominatedPhiUsers);
+ return new SomethingAssumedDominance(dominatedUsers, dominatedPhiUsers);
}
- public static UnknownNonNullDominance unknown() {
- return UnknownNonNullDominance.getInstance();
+ public static UnknownAssumedDominance unknown() {
+ return UnknownAssumedDominance.getInstance();
}
static class Builder {
- private final Value nonNullValue;
+ private final Value assumedValue;
private final Set<Instruction> dominatedUsers = Sets.newIdentityHashSet();
private final Map<Phi, IntList> dominatedPhiUsers = new IdentityHashMap<>();
- private Builder(Value nonNullValue) {
- this.nonNullValue = nonNullValue;
+ private Builder(Value assumedValue) {
+ this.assumedValue = assumedValue;
}
void addDominatedUser(Instruction user) {
- assert nonNullValue.uniqueUsers().contains(user);
+ assert assumedValue.uniqueUsers().contains(user);
assert !dominatedUsers.contains(user);
dominatedUsers.add(user);
}
void addDominatedPhiUser(Phi user, IntList dominatedPredecessorIndices) {
- assert nonNullValue.uniquePhiUsers().contains(user);
+ assert assumedValue.uniquePhiUsers().contains(user);
assert !dominatedPhiUsers.containsKey(user);
dominatedPhiUsers.put(user, dominatedPredecessorIndices);
}
- NonNullDominance build() {
+ AssumedDominance build() {
if (dominatedUsers.isEmpty() && dominatedPhiUsers.isEmpty()) {
return nothing();
}
- assert dominatedUsers.size() < nonNullValue.uniqueUsers().size()
- || dominatedPhiUsers.size() < nonNullValue.uniquePhiUsers().size();
+ assert dominatedUsers.size() < assumedValue.uniqueUsers().size()
+ || dominatedPhiUsers.size() < assumedValue.uniquePhiUsers().size();
return something(dominatedUsers, dominatedPhiUsers);
}
}
}
- static class EverythingNonNullDominance extends NonNullDominance {
+ static class EverythingAssumedDominance extends AssumedDominance {
- private static final EverythingNonNullDominance INSTANCE = new EverythingNonNullDominance();
+ private static final EverythingAssumedDominance INSTANCE = new EverythingAssumedDominance();
- private EverythingNonNullDominance() {}
+ private EverythingAssumedDominance() {}
- public static EverythingNonNullDominance getInstance() {
+ public static EverythingAssumedDominance getInstance() {
return INSTANCE;
}
@@ -719,14 +745,14 @@
}
}
- static class EverythingElseNonNullDominance extends NonNullDominance {
+ static class EverythingElseAssumedDominance extends AssumedDominance {
- private static final EverythingElseNonNullDominance INSTANCE =
- new EverythingElseNonNullDominance();
+ private static final EverythingElseAssumedDominance INSTANCE =
+ new EverythingElseAssumedDominance();
- private EverythingElseNonNullDominance() {}
+ private EverythingElseAssumedDominance() {}
- public static EverythingElseNonNullDominance getInstance() {
+ public static EverythingElseAssumedDominance getInstance() {
return INSTANCE;
}
@@ -736,13 +762,13 @@
}
}
- static class NothingNonNullDominance extends NonNullDominance {
+ static class NothingAssumedDominance extends AssumedDominance {
- private static final NothingNonNullDominance INSTANCE = new NothingNonNullDominance();
+ private static final NothingAssumedDominance INSTANCE = new NothingAssumedDominance();
- private NothingNonNullDominance() {}
+ private NothingAssumedDominance() {}
- public static NothingNonNullDominance getInstance() {
+ public static NothingAssumedDominance getInstance() {
return INSTANCE;
}
@@ -752,12 +778,12 @@
}
}
- static class SomethingNonNullDominance extends NonNullDominance {
+ static class SomethingAssumedDominance extends AssumedDominance {
private final Set<Instruction> dominatedUsers;
private final Map<Phi, IntList> dominatedPhiUsers;
- SomethingNonNullDominance(
+ SomethingAssumedDominance(
Set<Instruction> dominatedUsers, Map<Phi, IntList> dominatedPhiUsers) {
this.dominatedUsers = dominatedUsers;
this.dominatedPhiUsers = dominatedPhiUsers;
@@ -777,18 +803,18 @@
}
@Override
- SomethingNonNullDominance asSomething() {
+ SomethingAssumedDominance asSomething() {
return this;
}
}
- static class UnknownNonNullDominance extends NonNullDominance {
+ static class UnknownAssumedDominance extends AssumedDominance {
- private static final UnknownNonNullDominance INSTANCE = new UnknownNonNullDominance();
+ private static final UnknownAssumedDominance INSTANCE = new UnknownAssumedDominance();
- private UnknownNonNullDominance() {}
+ private UnknownAssumedDominance() {}
- public static UnknownNonNullDominance getInstance() {
+ public static UnknownAssumedDominance getInstance() {
return INSTANCE;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index d05d64e..9708120 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -19,8 +19,6 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.Assume;
-import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
-import com.android.tools.r8.ir.code.Assume.NonNullAssumption;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -112,7 +110,7 @@
SingleResolutionResult resolutionResult =
appView
.appInfo()
- .resolveMethod(invokedMethod, invoke.isInvokeInterface())
+ .resolveMethod(invokedMethod, invoke.getInterfaceBit())
.asSingleResolution();
if (resolutionResult == null) {
return;
@@ -309,7 +307,7 @@
return;
}
Set<Value> affectedValues = Sets.newIdentityHashSet();
- List<Assume<?>> assumeInstructions = new LinkedList<>();
+ List<Assume> assumeInstructions = new LinkedList<>();
List<Instruction> constants = new LinkedList<>();
int argumentsSeen = 0;
InstructionListIterator iterator = code.entryBlock().listIterator(code);
@@ -356,7 +354,7 @@
specializedArg = code.createValue(originalArg.getType());
affectedValues.addAll(originalArg.affectedValues());
originalArg.replaceUsers(specializedArg);
- Assume<DynamicTypeAssumption> assumeType =
+ Assume assumeType =
Assume.createAssumeDynamicTypeInstruction(
dynamicUpperBoundType, null, specializedArg, originalArg, instr, appView);
assumeType.setPosition(instr.getPosition());
@@ -372,7 +370,7 @@
code.createValue(specializedArg.getType().asReferenceType().asMeetWithNotNull());
affectedValues.addAll(specializedArg.affectedValues());
specializedArg.replaceUsers(nonNullArg);
- Assume<NonNullAssumption> assumeNotNull =
+ Assume assumeNotNull =
Assume.createAssumeNonNullInstruction(nonNullArg, specializedArg, instr, appView);
assumeNotNull.setPosition(instr.getPosition());
assumeInstructions.add(assumeNotNull);
@@ -387,7 +385,7 @@
+ code.method().method.getArity()
+ ", static: "
+ code.method().isStatic();
- // After packed Argument instructions, add Assume<?> and constant instructions.
+ // After packed Argument instructions, add Assume and constant instructions.
assert !iterator.peekPrevious().isArgument();
iterator.previous();
assert iterator.peekPrevious().isArgument();
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 52844be..172a6b3 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
@@ -176,8 +176,8 @@
public static void removeAssumeInstructions(AppView<?> appView, IRCode code) {
// We need to update the types of all values whose definitions depend on a non-null value.
- // This is needed to preserve soundness of the types after the Assume<NonNullAssumption>
- // instructions have been removed.
+ // This is needed to preserve soundness of the types after the Assume instructions have been
+ // removed.
//
// As an example, consider a check-cast instruction on the form "z = (T) y". If y used to be
// defined by a NonNull instruction, then the type analysis could have used this information
@@ -201,7 +201,7 @@
//
// x.foo()
if (instruction.isAssume()) {
- Assume<?> assumeInstruction = instruction.asAssume();
+ Assume assumeInstruction = instruction.asAssume();
Value src = assumeInstruction.src();
Value dest = assumeInstruction.outValue();
@@ -1507,8 +1507,8 @@
TypeElement dynamicType =
aliasedValue
.definition
- .asAssumeDynamicType()
- .getAssumption()
+ .asAssume()
+ .getDynamicTypeAssumption()
.getDynamicUpperBoundType();
if (dynamicType.isDefinitelyNull()) {
result = InstanceOfResult.FALSE;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 5893a3b..c4bf62f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
@@ -113,6 +114,7 @@
@Override
public boolean passesInliningConstraints(
InvokeMethod invoke,
+ SingleResolutionResult resolutionResult,
ProgramMethod singleTarget,
Reason reason,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
@@ -167,7 +169,7 @@
}
// Abort inlining attempt if method -> target access is not right.
- if (!inliner.hasInliningAccess(method, singleTarget)) {
+ if (resolutionResult.isAccessibleFrom(method, appView.appInfo()).isPossiblyFalse()) {
whyAreYouNotInliningReporter.reportInaccessible();
return false;
}
@@ -254,6 +256,7 @@
@Override
public InlineAction computeInlining(
InvokeMethod invoke,
+ SingleResolutionResult resolutionResult,
ProgramMethod singleTarget,
ProgramMethod context,
ClassInitializationAnalysis classInitializationAnalysis,
@@ -277,7 +280,8 @@
return null;
}
- if (!passesInliningConstraints(invoke, singleTarget, reason, whyAreYouNotInliningReporter)) {
+ if (!passesInliningConstraints(
+ invoke, resolutionResult, singleTarget, reason, whyAreYouNotInliningReporter)) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 8117d6c..7752d6c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Assume;
-import com.android.tools.r8.ir.code.Assume.NonNullAssumption;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.DominatorTree;
@@ -78,7 +77,7 @@
// ...
// non_null_rcv <- non-null rcv_c // <- Update the input rcv to the non-null, too.
if (current.isAssumeNonNull()) {
- Assume<NonNullAssumption> nonNull = current.asAssumeNonNull();
+ Assume nonNull = current.asAssume();
Instruction origin = nonNull.origin();
if (origin.isInvokeInterface()
&& !origin.asInvokeInterface().getReceiver().hasLocalInfo()
@@ -126,7 +125,11 @@
DexMethod invokedMethod = invoke.getInvokedMethod();
DexEncodedMethod newSingleTarget =
InvokeVirtual.lookupSingleTarget(
- appView, context, invoke.getReceiver(), invokedMethod);
+ appView,
+ context,
+ invoke.getReceiver().getDynamicUpperBoundType(appView),
+ invoke.getReceiver().getDynamicLowerBoundType(appView),
+ invokedMethod);
if (newSingleTarget == singleTarget) {
it.replaceCurrentInstruction(
new InvokeVirtual(invokedMethod, invoke.outValue(), invoke.arguments()));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index 4aabc5b..6e2d9c7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Assume;
-import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.IRCode;
@@ -143,7 +142,7 @@
outValue.replaceUsers(specializedOutValue);
// Insert AssumeDynamicType instruction.
- Assume<DynamicTypeAssumption> assumeInstruction =
+ Assume assumeInstruction =
Assume.createAssumeDynamicTypeInstruction(
dynamicUpperBoundType,
dynamicLowerBoundType,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index a48dece..eea6b7c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
@@ -42,6 +43,7 @@
@Override
public boolean passesInliningConstraints(
InvokeMethod invoke,
+ SingleResolutionResult resolutionResult,
ProgramMethod candidate,
Reason reason,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
@@ -60,15 +62,18 @@
@Override
public InlineAction computeInlining(
InvokeMethod invoke,
+ SingleResolutionResult resolutionResult,
ProgramMethod singleTarget,
ProgramMethod context,
ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- return computeForInvoke(invoke, whyAreYouNotInliningReporter);
+ return computeForInvoke(invoke, resolutionResult, whyAreYouNotInliningReporter);
}
private InlineAction computeForInvoke(
- InvokeMethod invoke, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
+ InvokeMethod invoke,
+ SingleResolutionResult resolutionResult,
+ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
Inliner.InliningInfo info = invokesToInline.get(invoke);
if (info == null) {
return null;
@@ -80,7 +85,7 @@
// with neverInline() flag.
assert !info.target.getDefinition().getOptimizationInfo().neverInline();
assert passesInliningConstraints(
- invoke, info.target, Reason.FORCE, whyAreYouNotInliningReporter);
+ invoke, resolutionResult, info.target, Reason.FORCE, whyAreYouNotInliningReporter);
return new InlineAction(info.target, invoke, Reason.FORCE);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index abf12f7..10d17e7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -139,7 +139,7 @@
SingleResolutionResult resolutionResult =
appInfoWithLiveness
- .resolveMethod(invoke.getInvokedMethod(), invoke.isInvokeInterface())
+ .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
.asSingleResolution();
if (resolutionResult == null
|| resolutionResult
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index fe6cbb4..17cea8b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.proto.ProtoInliningReasonStrategy;
import com.android.tools.r8.ir.analysis.type.Nullability;
@@ -199,29 +200,6 @@
return false;
}
- boolean hasInliningAccess(ProgramMethod context, ProgramMethod target) {
- if (!isVisibleWithFlags(target.getHolderType(), context, target.getDefinition().accessFlags)) {
- return false;
- }
- // The class needs also to be visible for us to have access.
- return isVisibleWithFlags(target.getHolderType(), context, target.getHolder().accessFlags);
- }
-
- private boolean isVisibleWithFlags(DexType target, ProgramMethod context, AccessFlags<?> flags) {
- if (flags.isPublic()) {
- return true;
- }
- if (flags.isPrivate()) {
- return NestUtils.sameNest(target, context.getHolderType(), appView);
- }
- if (flags.isProtected()) {
- return appView.appInfo().isSubtype(context.getHolderType(), target)
- || target.isSamePackage(context.getHolderType());
- }
- // package-private
- return target.isSamePackage(context.getHolderType());
- }
-
public synchronized boolean isDoubleInlineSelectedTarget(ProgramMethod method) {
return doubleInlineSelectedTargets.contains(method);
}
@@ -975,6 +953,17 @@
if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
// TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget()!
+
+ SingleResolutionResult resolutionResult =
+ appView
+ .appInfo()
+ .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
+ .asSingleResolution();
+ if (resolutionResult == null) {
+ continue;
+ }
+
+ // TODO(b/156853206): Should not duplicate resolution.
ProgramMethod singleTarget = oracle.lookupSingleTarget(invoke, context);
if (singleTarget == null) {
WhyAreYouNotInliningReporter.handleInvokeWithUnknownTarget(invoke, appView, context);
@@ -989,6 +978,7 @@
InlineAction action =
oracle.computeInlining(
invoke,
+ resolutionResult,
singleTarget,
context,
classInitializationAnalysis,
@@ -1084,7 +1074,7 @@
blockIterator.next();
}
} else if (current.isAssumeDynamicType()) {
- assumeDynamicTypeRemover.removeIfMarked(current.asAssumeDynamicType(), iterator);
+ assumeDynamicTypeRemover.removeIfMarked(current.asAssume(), iterator);
}
}
}
@@ -1160,7 +1150,7 @@
// Add non-null IRs only to the inlinee blocks.
if (options.enableNonNullTracking) {
- Assumer nonNullTracker = new NonNullTracker(appView);
+ Assumer nonNullTracker = new AssumeInserter(appView);
applyAssumerToInlinee(nonNullTracker, code, blockIterator, block, inlineeBlocks, timing);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
index a9e4736..5518ab7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
@@ -23,12 +24,14 @@
boolean passesInliningConstraints(
InvokeMethod invoke,
+ SingleResolutionResult resolutionResult,
ProgramMethod candidate,
Reason reason,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
InlineAction computeInlining(
InvokeMethod invoke,
+ SingleResolutionResult resolutionResult,
ProgramMethod singleTarget,
ProgramMethod context,
ClassInitializationAnalysis classInitializationAnalysis,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 8222958..51c1c01 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -357,13 +357,10 @@
return;
}
- // Check if the field is pinned. In that case, it could be written by reflection.
- if (appView.appInfo().isPinned(target.field)) {
- return;
- }
-
AbstractValue abstractValue;
- if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
+ if (field.type.isAlwaysNull(appView)) {
+ abstractValue = appView.abstractValueFactory().createSingleNumberValue(0);
+ } else if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
abstractValue = target.getOptimizationInfo().getAbstractValue();
if (abstractValue.isUnknown() && !target.isStatic()) {
AbstractValue abstractReceiverValue =
@@ -485,12 +482,15 @@
private void replaceInstancePutByNullCheckIfNeverRead(
IRCode code, InstructionListIterator iterator, InstancePut current) {
- DexEncodedField target = appView.appInfo().resolveField(current.getField()).getResolvedField();
- if (target == null || appView.appInfo().isFieldRead(target)) {
+ DexEncodedField field = appView.appInfo().resolveField(current.getField()).getResolvedField();
+ if (field == null || field.isStatic()) {
return;
}
- if (target.isStatic()) {
+ // If the field is read, we can't remove the instance-put unless the value of the field is known
+ // to be null (in which case the instance-put is a no-op because it assigns the field the same
+ // value as its default value).
+ if (!field.type().isAlwaysNull(appView) && appView.appInfo().isFieldRead(field)) {
return;
}
@@ -500,11 +500,14 @@
private void replaceStaticPutByInitClassIfNeverRead(
IRCode code, InstructionListIterator iterator, StaticPut current) {
DexEncodedField field = appView.appInfo().resolveField(current.getField()).getResolvedField();
- if (field == null || appView.appInfo().isFieldRead(field)) {
+ if (field == null || !field.isStatic()) {
return;
}
- if (!field.isStatic()) {
+ // If the field is read, we can't remove the static-put unless the value of the field is known
+ // to be null (in which case the static-put is a no-op because it assigns the field the same
+ // value as its default value).
+ if (!field.type().isAlwaysNull(appView) && appView.appInfo().isFieldRead(field)) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
index 3cd05c5..9db947a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
@@ -57,7 +57,7 @@
assert encodedMethod.isPrivateMethod();
// Call to private method which has now to be interface/virtual
// (Now call to nest member private method).
- if (invoke.isInterface()) {
+ if (invoke.getInterfaceBit()) {
iterator.replaceCurrentInstruction(
new InvokeInterface(method, invoke.outValue(), invoke.inValues()));
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 0ac7f7b..d9eb7f2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -66,14 +66,11 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.ProgramMethodEquivalence;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
-import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodMultisetBuilder;
+import com.android.tools.r8.utils.collections.ProgramMethodMultiset;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.Multiset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -98,10 +95,9 @@
* where each list of methods corresponds to methods containing an outlining candidate.
* <li>Second, {@link Outliner#selectMethodsForOutlining()} is called to retain the lists of
* methods found in the first step that are large enough (see {@link InternalOptions#outline}
- * {@link OutlineOptions#threshold}), and the methods to be further analyzed for outlining is
- * returned by {@link Outliner#buildMethodsSelectedForOutlining}. Each selected method is then
- * converted back to IR and passed to {@link Outliner#identifyOutlineSites(IRCode)}, which
- * then stores concrete outlining candidates in {@link Outliner#outlineSites}.
+ * {@link OutlineOptions#threshold}). Each selected method is then converted back to IR and
+ * passed to {@link Outliner#identifyOutlineSites(IRCode)}, which then stores concrete
+ * outlining candidates in {@link Outliner#outlineSites}.
* <li>Third, {@link Outliner#buildOutlinerClass(DexType)} is called to construct the <em>outline
* support class</em> containing a static helper method for each outline candidate that occurs
* frequently enough. Each selected method is then converted to IR, passed to {@link
@@ -112,10 +108,8 @@
public class Outliner {
/** Result of first step (see {@link Outliner#createOutlineMethodIdentifierGenerator()}. */
- private final List<Multiset<Wrapper<ProgramMethod>>> candidateMethodLists = new ArrayList<>();
- /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
- private final LongLivedProgramMethodSetBuilder<?> methodsSelectedForOutlining =
- LongLivedProgramMethodSetBuilder.create();
+ private final List<LongLivedProgramMethodMultisetBuilder> candidateMethodLists =
+ new ArrayList<>();
/** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
private final Map<Outline, List<ProgramMethod>> outlineSites = new HashMap<>();
/** Result of third step (see {@link Outliner#buildOutlinerClass(DexType)}. */
@@ -1138,12 +1132,12 @@
// TODO(sgjesse): This does not take several usages in the same method into account.
private class OutlineMethodIdentifier extends OutlineSpotter {
- private final Map<Outline, Multiset<Wrapper<ProgramMethod>>> candidateMap;
+ private final Map<Outline, LongLivedProgramMethodMultisetBuilder> candidateMap;
OutlineMethodIdentifier(
ProgramMethod method,
BasicBlock block,
- Map<Outline, Multiset<Wrapper<ProgramMethod>>> candidateMap) {
+ Map<Outline, LongLivedProgramMethodMultisetBuilder> candidateMap) {
super(method, block);
this.candidateMap = candidateMap;
}
@@ -1151,14 +1145,12 @@
@Override
protected void handle(int start, int end, Outline outline) {
synchronized (candidateMap) {
- candidateMap
- .computeIfAbsent(outline, this::addOutlineMethodList)
- .add(ProgramMethodEquivalence.get().wrap(method));
+ candidateMap.computeIfAbsent(outline, this::addOutlineMethodList).add(method);
}
}
- private Multiset<Wrapper<ProgramMethod>> addOutlineMethodList(Outline outline) {
- Multiset<Wrapper<ProgramMethod>> result = HashMultiset.create();
+ private LongLivedProgramMethodMultisetBuilder addOutlineMethodList(Outline outline) {
+ LongLivedProgramMethodMultisetBuilder result = LongLivedProgramMethodMultisetBuilder.create();
candidateMethodLists.add(result);
return result;
}
@@ -1284,14 +1276,19 @@
// out-value of invokes to null), this map must not be used except for identifying methods
// potentially relevant to outlining. OutlineMethodIdentifier will add method lists to
// candidateMethodLists whenever it adds an entry to candidateMap.
- Map<Outline, Multiset<Wrapper<ProgramMethod>>> candidateMap = new HashMap<>();
+ Map<Outline, LongLivedProgramMethodMultisetBuilder> candidateMap = new HashMap<>();
assert candidateMethodLists.isEmpty();
assert outlineMethodIdentifierGenerator == null;
outlineMethodIdentifierGenerator =
code -> {
- assert !code.method().getCode().isOutlineCode();
+ ProgramMethod context = code.context();
+ assert !context.getDefinition().getCode().isOutlineCode();
+ if (appView.options().featureSplitConfiguration != null
+ && appView.options().featureSplitConfiguration.isInFeature(context.getHolder())) {
+ return;
+ }
for (BasicBlock block : code.blocks) {
- new OutlineMethodIdentifier(code.context(), block, candidateMap).process();
+ new OutlineMethodIdentifier(context, block, candidateMap).process();
}
};
}
@@ -1304,31 +1301,26 @@
}
public void identifyOutlineSites(IRCode code) {
- assert !code.method().getCode().isOutlineCode();
- DexProgramClass clazz = code.context().getHolder();
- if (appView.options().featureSplitConfiguration != null
- && appView.options().featureSplitConfiguration.isInFeature(clazz)) {
- return;
- }
+ ProgramMethod context = code.context();
+ assert !context.getDefinition().getCode().isOutlineCode();
+ assert appView.options().featureSplitConfiguration == null
+ || !appView.options().featureSplitConfiguration.isInFeature(context.getHolder());
for (BasicBlock block : code.blocks) {
- new OutlineSiteIdentifier(code.context(), block).process();
+ new OutlineSiteIdentifier(context, block).process();
}
}
- public boolean selectMethodsForOutlining() {
- assert methodsSelectedForOutlining.isEmpty();
+ public ProgramMethodSet selectMethodsForOutlining() {
+ ProgramMethodSet methodsSelectedForOutlining = ProgramMethodSet.create();
assert outlineSites.isEmpty();
- for (Multiset<Wrapper<ProgramMethod>> outlineMethods : candidateMethodLists) {
+ for (LongLivedProgramMethodMultisetBuilder outlineMethods : candidateMethodLists) {
if (outlineMethods.size() >= appView.options().outline.threshold) {
- outlineMethods.forEach(wrapper -> methodsSelectedForOutlining.add(wrapper.get()));
+ ProgramMethodMultiset multiset = outlineMethods.build(appView);
+ multiset.forEachEntry((method, ignore) -> methodsSelectedForOutlining.add(method));
}
}
candidateMethodLists.clear();
- return !methodsSelectedForOutlining.isEmpty();
- }
-
- public ProgramMethodSet buildMethodsSelectedForOutlining() {
- return methodsSelectedForOutlining.build(appView);
+ return methodsSelectedForOutlining;
}
public DexProgramClass buildOutlinerClass(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 6077882..85f0274 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -9,9 +9,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -23,7 +21,6 @@
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
-import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.FieldInstruction;
@@ -34,7 +31,6 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
-import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.Timing;
@@ -103,17 +99,9 @@
private static final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
private final AppView<AppInfoWithLiveness> appView;
- private final TypeChecker typeChecker;
-
- private int numberOfInstanceGetOrInstancePutWithNullReceiver = 0;
- private int numberOfArrayInstructionsWithNullArray = 0;
- private int numberOfInvokesWithNullArgument = 0;
- private int numberOfInvokesWithNullReceiver = 0;
- private int numberOfMonitorWithNullReceiver = 0;
public UninstantiatedTypeOptimization(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- this.typeChecker = new TypeChecker(appView);
}
public UninstantiatedTypeOptimizationGraphLense run(
@@ -353,32 +341,12 @@
if (instruction.throwsOnNullInput()) {
Value couldBeNullValue = instruction.getNonNullInput();
if (isThrowNullCandidate(couldBeNullValue, instruction, appView, code.context())) {
- if (instruction.isInstanceGet() || instruction.isInstancePut()) {
- ++numberOfInstanceGetOrInstancePutWithNullReceiver;
- } else if (instruction.isInvokeMethodWithReceiver()) {
- ++numberOfInvokesWithNullReceiver;
- } else if (instruction.isArrayGet()
- || instruction.isArrayPut()
- || instruction.isArrayLength()) {
- ++numberOfArrayInstructionsWithNullArray;
- } else if (instruction.isMonitor()) {
- ++numberOfMonitorWithNullReceiver;
- } else {
- assert false;
- }
instructionIterator.replaceCurrentInstructionWithThrowNull(
appView, code, blockIterator, blocksToBeRemoved, valuesToNarrow);
continue;
}
}
- if (instruction.isFieldInstruction()) {
- rewriteFieldInstruction(
- instruction.asFieldInstruction(),
- instructionIterator,
- code,
- assumeDynamicTypeRemover,
- valuesToNarrow);
- } else if (instruction.isInvokeMethod()) {
+ if (instruction.isInvokeMethod()) {
rewriteInvoke(
instruction.asInvokeMethod(),
blockIterator,
@@ -421,75 +389,6 @@
return true;
}
- public void logResults() {
- assert Log.ENABLED;
- Log.info(
- getClass(),
- "Number of instance-get/instance-put with null receiver: %s",
- numberOfInstanceGetOrInstancePutWithNullReceiver);
- Log.info(
- getClass(),
- "Number of array instructions with null reference: %s",
- numberOfArrayInstructionsWithNullArray);
- Log.info(
- getClass(), "Number of invokes with null argument: %s", numberOfInvokesWithNullArgument);
- Log.info(
- getClass(), "Number of invokes with null receiver: %s", numberOfInvokesWithNullReceiver);
- Log.info(
- getClass(), "Number of monitor with null receiver: %s", numberOfMonitorWithNullReceiver);
- }
-
- // instance-{get|put} with a null receiver has already been rewritten to `throw null`.
- // At this point, field-instruction whose target field type is uninstantiated will be handled.
- private void rewriteFieldInstruction(
- FieldInstruction instruction,
- InstructionListIterator instructionIterator,
- IRCode code,
- AssumeDynamicTypeRemover assumeDynamicTypeRemover,
- Set<Value> affectedValues) {
- ProgramMethod context = code.context();
- DexField field = instruction.getField();
- DexType fieldType = field.type;
- if (fieldType.isAlwaysNull(appView)) {
- // TODO(b/123857022): Should be possible to use definitionFor().
- DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
- if (encodedField == null) {
- return;
- }
-
- boolean instructionCanBeRemoved = !instruction.instructionInstanceCanThrow(appView, context);
-
- BasicBlock block = instruction.getBlock();
- if (instruction.isFieldPut()) {
- if (!typeChecker.checkFieldPut(instruction)) {
- // Broken type hierarchy. See FieldTypeTest#test_brokenTypeHierarchy.
- assert appView.options().testing.allowTypeErrors;
- return;
- }
-
- // We know that the right-hand side must be null, so this is a no-op.
- if (instructionCanBeRemoved) {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- } else {
- if (instructionCanBeRemoved) {
- // Replace the field read by the constant null.
- assumeDynamicTypeRemover.markUsersForRemoval(instruction.outValue());
- affectedValues.addAll(instruction.outValue().affectedValues());
- instructionIterator.replaceCurrentInstruction(code.createConstNull());
- } else {
- replaceOutValueByNull(
- instruction, instructionIterator, code, assumeDynamicTypeRemover, affectedValues);
- }
- }
-
- if (block.hasCatchHandlers()) {
- // This block can no longer throw.
- block.getCatchHandlers().getUniqueTargets().forEach(BasicBlock::unlinkCatchHandler);
- }
- }
- }
-
// invoke instructions with a null receiver has already been rewritten to `throw null`.
// At this point, we attempt to explore non-null-param-or-throw optimization info and replace
// the invocation with `throw null` if an argument is known to be null and the method is going to
@@ -514,7 +413,6 @@
if (argument.isAlwaysNull(appView) && facts.get(i)) {
instructionIterator.replaceCurrentInstructionWithThrowNull(
appView, code, blockIterator, blocksToBeRemoved, affectedValues);
- ++numberOfInvokesWithNullArgument;
return;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 5e378c2..da8c4cc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -19,8 +19,10 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.AliasedValueConfiguration;
import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration;
@@ -281,6 +283,16 @@
if (user.isInvokeMethod()) {
InvokeMethod invokeMethod = user.asInvokeMethod();
+ SingleResolutionResult resolutionResult =
+ appView
+ .appInfo()
+ .resolveMethod(invokeMethod.getInvokedMethod(), invokeMethod.getInterfaceBit())
+ .asSingleResolution();
+ if (resolutionResult == null) {
+ return user; // Not eligible.
+ }
+
+ // TODO(b/156853206): Avoid duplicating resolution.
DexEncodedMethod singleTargetMethod = invokeMethod.lookupSingleTarget(appView, method);
if (singleTargetMethod == null) {
return user; // Not eligible.
@@ -320,7 +332,7 @@
InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
InliningInfo inliningInfo =
isEligibleDirectVirtualMethodCall(
- invoke, singleTarget, indirectUsers, defaultOracle);
+ invoke, resolutionResult, singleTarget, indirectUsers, defaultOracle);
if (inliningInfo != null) {
methodCallsOnInstance.put(invoke, inliningInfo);
continue;
@@ -529,8 +541,11 @@
continue;
}
+ ClassTypeElement exactReceiverType =
+ ClassTypeElement.create(eligibleClass.type, Nullability.definitelyNotNull(), appView);
ProgramMethod singleTarget =
- invoke.lookupSingleProgramTarget(appView, method, eligibleInstance);
+ invoke.lookupSingleProgramTarget(
+ appView, method, exactReceiverType, exactReceiverType);
if (singleTarget == null || !indirectMethodCallsOnInstance.contains(singleTarget)) {
throw new IllegalClassInlinerStateException();
}
@@ -875,6 +890,7 @@
private InliningInfo isEligibleDirectVirtualMethodCall(
InvokeMethodWithReceiver invoke,
+ SingleResolutionResult resolutionResult,
ProgramMethod singleTarget,
Set<Instruction> indirectUsers,
Supplier<InliningOracle> defaultOracle) {
@@ -892,7 +908,11 @@
if (singleTarget.getDefinition().isLibraryMethodOverride().isTrue()) {
InliningOracle inliningOracle = defaultOracle.get();
if (!inliningOracle.passesInliningConstraints(
- invoke, singleTarget, Reason.SIMPLE, NopWhyAreYouNotInliningReporter.getInstance())) {
+ invoke,
+ resolutionResult,
+ singleTarget,
+ Reason.SIMPLE,
+ NopWhyAreYouNotInliningReporter.getInstance())) {
return null;
}
}
@@ -916,13 +936,16 @@
Pair<Type, DexMethod> invokeInfo = eligibility.callsReceiver.get(0);
Type invokeType = invokeInfo.getFirst();
DexMethod indirectlyInvokedMethod = invokeInfo.getSecond();
- ResolutionResult resolutionResult =
- appView.appInfo().resolveMethodOn(eligibleClass, indirectlyInvokedMethod);
- if (!resolutionResult.isSingleResolution()) {
+ SingleResolutionResult indirectResolutionResult =
+ appView
+ .appInfo()
+ .resolveMethodOn(eligibleClass, indirectlyInvokedMethod)
+ .asSingleResolution();
+ if (indirectResolutionResult == null) {
return null;
}
ProgramMethod indirectSingleTarget =
- resolutionResult.asSingleResolution().getResolutionPair().asProgramMethod();
+ indirectResolutionResult.getResolutionPair().asProgramMethod();
if (!isEligibleIndirectVirtualMethodCall(
indirectlyInvokedMethod, invokeType, indirectSingleTarget)) {
return null;
@@ -1191,24 +1214,28 @@
return false;
}
+ SingleResolutionResult resolutionResult =
+ appView
+ .appInfo()
+ .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
+ .asSingleResolution();
+ if (resolutionResult == null) {
+ return false;
+ }
+
// Check if the method is inline-able by standard inliner.
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method);
+ // TODO(b/156853206): Should not duplicate resolution.
+ ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, method);
if (singleTarget == null) {
return false;
}
- DexProgramClass holder = asProgramClassOrNull(appView.definitionForHolder(singleTarget));
- if (holder == null) {
- return false;
- }
-
- ProgramMethod singleTargetMethod = new ProgramMethod(holder, singleTarget);
-
InliningOracle oracle = defaultOracle.get();
InlineAction inlineAction =
oracle.computeInlining(
invoke,
- singleTargetMethod,
+ resolutionResult,
+ singleTarget,
method,
ClassInitializationAnalysis.trivial(),
NopWhyAreYouNotInliningReporter.getInstance());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index c179446..379fc28 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -446,20 +446,16 @@
if (dexClass.type != factory.enumType) {
return Reason.UNSUPPORTED_LIBRARY_CALL;
}
- // TODO(b/147860220): Methods toString(), name(), compareTo(), EnumSet and EnumMap may
- // be interesting to model. A the moment rewrite only Enum#ordinal() and Enum#valueOf.
- if (debugLogEnabled) {
- if (singleTarget == factory.enumMethods.compareTo) {
- return Reason.COMPARE_TO_INVOKE;
- }
- if (singleTarget == factory.enumMethods.name) {
- return Reason.NAME_INVOKE;
- }
- if (singleTarget == factory.enumMethods.toString) {
- return Reason.TO_STRING_INVOKE;
- }
- }
- if (singleTarget == factory.enumMethods.ordinal) {
+ // TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
+ if (singleTarget == factory.enumMethods.compareTo) {
+ return Reason.ELIGIBLE;
+ } else if (singleTarget == factory.enumMethods.equals) {
+ return Reason.ELIGIBLE;
+ } else if (singleTarget == factory.enumMethods.name) {
+ return Reason.ELIGIBLE;
+ } else if (singleTarget == factory.enumMethods.toString) {
+ return Reason.ELIGIBLE;
+ } else if (singleTarget == factory.enumMethods.ordinal) {
return Reason.ELIGIBLE;
} else if (singleTarget == factory.enumMethods.constructor) {
// Enum constructor call is allowed only if first call of an enum initializer.
@@ -664,8 +660,6 @@
VALUE_OF_INVOKE,
VALUES_INVOKE,
COMPARE_TO_INVOKE,
- TO_STRING_INVOKE,
- NAME_INVOKE,
UNSUPPORTED_LIBRARY_CALL,
MISSING_INFO_MAP,
INVALID_FIELD_PUT,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 6f6fba1..541cd92 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.ArrayAccess;
+import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -46,32 +47,32 @@
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Function;
public class EnumUnboxingRewriter {
public static final String ENUM_UNBOXING_UTILITY_CLASS_NAME = "$r8$EnumUnboxingUtility";
- public static final String ENUM_UNBOXING_UTILITY_ORDINAL = "$enumboxing$ordinal";
- private static final String ENUM_UNBOXING_UTILITY_VALUES = "$enumboxing$values";
+ public static final String ENUM_UNBOXING_UTILITY_METHOD_PREFIX = "$enumboxing$";
private static final int REQUIRED_CLASS_FILE_VERSION = 52;
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory factory;
private final EnumValueInfoMapCollection enumsToUnbox;
- private final Map<DexMethod, DexEncodedMethod> extraUtilityMethods = new ConcurrentHashMap<>();
+ private final Map<DexMethod, DexEncodedMethod> utilityMethods = new ConcurrentHashMap<>();
private final Map<DexField, DexEncodedField> extraUtilityFields = new ConcurrentHashMap<>();
private final DexMethod ordinalUtilityMethod;
+ private final DexMethod equalsUtilityMethod;
+ private final DexMethod compareToUtilityMethod;
private final DexMethod valuesUtilityMethod;
- private boolean requiresOrdinalUtilityMethod = false;
- private boolean requiresValuesUtilityMethod = false;
-
EnumUnboxingRewriter(AppView<AppInfoWithLiveness> appView, Set<DexType> enumsToUnbox) {
this.appView = appView;
this.factory = appView.dexItemFactory();
@@ -81,16 +82,28 @@
}
this.enumsToUnbox = builder.build();
+ // Custom methods for java.lang.Enum methods ordinal, equals and compareTo.
this.ordinalUtilityMethod =
factory.createMethod(
factory.enumUnboxingUtilityType,
factory.createProto(factory.intType, factory.intType),
- ENUM_UNBOXING_UTILITY_ORDINAL);
+ ENUM_UNBOXING_UTILITY_METHOD_PREFIX + "ordinal");
+ this.equalsUtilityMethod =
+ factory.createMethod(
+ factory.enumUnboxingUtilityType,
+ factory.createProto(factory.booleanType, factory.intType, factory.intType),
+ ENUM_UNBOXING_UTILITY_METHOD_PREFIX + "equals");
+ this.compareToUtilityMethod =
+ factory.createMethod(
+ factory.enumUnboxingUtilityType,
+ factory.createProto(factory.intType, factory.intType, factory.intType),
+ ENUM_UNBOXING_UTILITY_METHOD_PREFIX + "compareTo");
+ // Custom methods for generated field $VALUES initialization.
this.valuesUtilityMethod =
factory.createMethod(
factory.enumUnboxingUtilityType,
factory.createProto(factory.intArrayType, factory.intType),
- ENUM_UNBOXING_UTILITY_VALUES);
+ ENUM_UNBOXING_UTILITY_METHOD_PREFIX + "values");
}
public EnumValueInfoMapCollection getEnumsToUnbox() {
@@ -104,6 +117,7 @@
return Sets.newIdentityHashSet();
}
assert code.isConsistentSSABeforeTypesAreCorrect();
+ Map<Instruction, DexType> convertedEnums = new IdentityHashMap<>();
Set<Phi> affectedPhis = Sets.newIdentityHashSet();
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
@@ -113,14 +127,28 @@
if (instruction.isInvokeMethodWithReceiver()) {
InvokeMethodWithReceiver invokeMethod = instruction.asInvokeMethodWithReceiver();
DexMethod invokedMethod = invokeMethod.getInvokedMethod();
- if (invokedMethod == factory.enumMethods.ordinal
- && isEnumToUnboxOrInt(invokeMethod.getReceiver().getType())) {
- instruction =
- new InvokeStatic(
- ordinalUtilityMethod, invokeMethod.outValue(), invokeMethod.inValues());
- iterator.replaceCurrentInstruction(instruction);
- requiresOrdinalUtilityMethod = true;
- continue;
+ DexType enumType = getEnumTypeOrNull(invokeMethod.getReceiver(), convertedEnums);
+ if (enumType != null) {
+ if (invokedMethod == factory.enumMethods.ordinal) {
+ replaceEnumInvoke(
+ iterator, invokeMethod, ordinalUtilityMethod, m -> synthesizeOrdinalMethod());
+ continue;
+ } else if (invokedMethod == factory.enumMethods.equals) {
+ replaceEnumInvoke(
+ iterator, invokeMethod, equalsUtilityMethod, m -> synthesizeEqualsMethod());
+ continue;
+ } else if (invokedMethod == factory.enumMethods.compareTo) {
+ replaceEnumInvoke(
+ iterator, invokeMethod, compareToUtilityMethod, m -> synthesizeCompareToMethod());
+ continue;
+ } else if (invokedMethod == factory.enumMethods.name
+ || invokedMethod == factory.enumMethods.toString) {
+ DexMethod toStringMethod = computeDefaultToStringUtilityMethod(enumType);
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(
+ toStringMethod, invokeMethod.outValue(), invokeMethod.arguments()));
+ continue;
+ }
}
// TODO(b/147860220): rewrite also other enum methods.
} else if (instruction.isInvokeStatic()) {
@@ -138,11 +166,13 @@
rewrittenOutValue = code.createValue(TypeElement.getInt());
affectedPhis.addAll(outValue.uniquePhiUsers());
}
- iterator.replaceCurrentInstruction(
+ InvokeStatic invoke =
new InvokeStatic(
valueOfMethod,
rewrittenOutValue,
- Collections.singletonList(invokeStatic.inValues().get(1))));
+ Collections.singletonList(invokeStatic.inValues().get(1)));
+ iterator.replaceCurrentInstruction(invoke);
+ convertedEnums.put(invoke, enumType);
continue;
}
}
@@ -162,33 +192,39 @@
affectedPhis.addAll(staticGet.outValue().uniquePhiUsers());
EnumValueInfo enumValueInfo = enumValueInfoMap.getEnumValueInfo(staticGet.getField());
if (enumValueInfo == null && staticGet.getField().name == factory.enumValuesFieldName) {
- requiresValuesUtilityMethod = true;
+ utilityMethods.computeIfAbsent(
+ valuesUtilityMethod, m -> synthesizeValuesUtilityMethod());
DexField fieldValues = createValuesField(holder);
extraUtilityFields.computeIfAbsent(fieldValues, this::computeValuesEncodedField);
DexMethod methodValues = createValuesMethod(holder);
- extraUtilityMethods.computeIfAbsent(
+ utilityMethods.computeIfAbsent(
methodValues,
m -> computeValuesEncodedMethod(m, fieldValues, enumValueInfoMap.size()));
Value rewrittenOutValue =
code.createValue(
ArrayTypeElement.create(TypeElement.getInt(), definitelyNotNull()));
- iterator.replaceCurrentInstruction(
- new InvokeStatic(methodValues, rewrittenOutValue, ImmutableList.of()));
+ InvokeStatic invoke =
+ new InvokeStatic(methodValues, rewrittenOutValue, ImmutableList.of());
+ iterator.replaceCurrentInstruction(invoke);
+ convertedEnums.put(invoke, holder);
} else {
// Replace by ordinal + 1 for null check (null is 0).
assert enumValueInfo != null
: "Invalid read to " + staticGet.getField().name + ", error during enum analysis";
- iterator.replaceCurrentInstruction(
- code.createIntConstant(enumValueInfo.convertToInt()));
+ ConstNumber intConstant = code.createIntConstant(enumValueInfo.convertToInt());
+ iterator.replaceCurrentInstruction(intConstant);
+ convertedEnums.put(intConstant, holder);
}
}
}
// Rewrite array accesses from MyEnum[] (OBJECT) to int[] (INT).
if (instruction.isArrayAccess()) {
ArrayAccess arrayAccess = instruction.asArrayAccess();
- if (shouldRewriteArrayAccess(arrayAccess)) {
+ DexType enumType = getEnumTypeOrNull(arrayAccess);
+ if (enumType != null) {
instruction = arrayAccess.withMemberType(MemberType.INT);
iterator.replaceCurrentInstruction(instruction);
+ convertedEnums.put(instruction, enumType);
}
assert validateArrayAccess(arrayAccess);
}
@@ -197,6 +233,17 @@
return affectedPhis;
}
+ private void replaceEnumInvoke(
+ InstructionListIterator iterator,
+ InvokeMethodWithReceiver invokeMethod,
+ DexMethod method,
+ Function<DexMethod, DexEncodedMethod> synthesizor) {
+ utilityMethods.computeIfAbsent(method, synthesizor);
+ Instruction instruction =
+ new InvokeStatic(method, invokeMethod.outValue(), invokeMethod.arguments());
+ iterator.replaceCurrentInstruction(instruction);
+ }
+
private boolean validateArrayAccess(ArrayAccess arrayAccess) {
ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType();
if (arrayType == null) {
@@ -209,14 +256,16 @@
return true;
}
- private boolean isEnumToUnboxOrInt(TypeElement type) {
+ private DexType getEnumTypeOrNull(Value receiver, Map<Instruction, DexType> convertedEnums) {
+ TypeElement type = receiver.getType();
if (type.isInt()) {
- return true;
+ return convertedEnums.get(receiver.definition);
}
if (!type.isClassType()) {
- return false;
+ return null;
}
- return enumsToUnbox.containsEnum(type.asClassType().getClassType());
+ DexType enumType = type.asClassType().getClassType();
+ return enumsToUnbox.containsEnum(enumType) ? enumType : null;
}
public String compatibleName(DexType type) {
@@ -266,22 +315,36 @@
factory.enumUnboxingUtilityType,
factory.createProto(factory.intType, factory.stringType),
"valueOf" + compatibleName(type));
- extraUtilityMethods.computeIfAbsent(valueOf, m -> synthesizeValueOfUtilityMethod(m, type));
+ utilityMethods.computeIfAbsent(valueOf, m -> synthesizeValueOfUtilityMethod(m, type));
return valueOf;
}
- private boolean shouldRewriteArrayAccess(ArrayAccess arrayAccess) {
+ private DexMethod computeDefaultToStringUtilityMethod(DexType type) {
+ assert enumsToUnbox.containsEnum(type);
+ DexMethod toString =
+ factory.createMethod(
+ factory.enumUnboxingUtilityType,
+ factory.createProto(factory.stringType, factory.intType),
+ "toString" + compatibleName(type));
+ utilityMethods.computeIfAbsent(toString, m -> synthesizeToStringUtilityMethod(m, type));
+ return toString;
+ }
+
+ private DexType getEnumTypeOrNull(ArrayAccess arrayAccess) {
ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType();
if (arrayType == null) {
assert arrayAccess.array().getType().isNullType();
- return false;
+ return null;
}
if (arrayType.getNesting() != 1) {
- return false;
+ return null;
}
TypeElement baseType = arrayType.getBaseType();
- return baseType.isClassType()
- && enumsToUnbox.containsEnum(baseType.asClassType().getClassType());
+ if (!baseType.isClassType()) {
+ return null;
+ }
+ DexType classType = baseType.asClassType().getClassType();
+ return enumsToUnbox.containsEnum(classType) ? classType : null;
}
void synthesizeEnumUnboxingUtilityMethods(
@@ -289,16 +352,9 @@
throws ExecutionException {
// Synthesize a class which holds various utility methods that may be called from the IR
// rewriting. If any of these methods are not used, they will be removed by the Enqueuer.
- List<DexEncodedMethod> requiredMethods = new ArrayList<>(extraUtilityMethods.values());
+ List<DexEncodedMethod> requiredMethods = new ArrayList<>(utilityMethods.values());
// Sort for deterministic order.
requiredMethods.sort((m1, m2) -> m1.method.name.slowCompareTo(m2.method.name));
- if (requiresOrdinalUtilityMethod) {
- requiredMethods.add(synthesizeOrdinalMethod());
- }
- if (requiresValuesUtilityMethod) {
- requiredMethods.add(synthesizeValuesUtilityMethod());
- }
- // TODO(b/147860220): synthesize also other enum methods.
if (requiredMethods.isEmpty()) {
return;
}
@@ -340,6 +396,17 @@
DexProgramClass::checksumFromType);
}
+ private DexEncodedMethod synthesizeToStringUtilityMethod(DexMethod method, DexType enumType) {
+ CfCode cfCode =
+ new EnumUnboxingCfCodeProvider.EnumUnboxingDefaultToStringCfCodeProvider(
+ appView,
+ factory.enumUnboxingUtilityType,
+ enumType,
+ enumsToUnbox.getEnumValueInfoMap(enumType))
+ .generateCfCode();
+ return synthesizeUtilityMethod(cfCode, method, false);
+ }
+
private DexEncodedMethod synthesizeValueOfUtilityMethod(DexMethod method, DexType enumType) {
CfCode cfCode =
new EnumUnboxingCfCodeProvider.EnumUnboxingValueOfCfCodeProvider(
@@ -348,14 +415,7 @@
enumType,
enumsToUnbox.getEnumValueInfoMap(enumType))
.generateCfCode();
- return new DexEncodedMethod(
- method,
- synthesizedMethodAccessFlags(false),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- cfCode,
- REQUIRED_CLASS_FILE_VERSION,
- true);
+ return synthesizeUtilityMethod(cfCode, method, false);
}
// TODO(b/150178516): Add a test for this case.
@@ -374,6 +434,19 @@
return synthesizeUtilityMethod(cfCode, ordinalUtilityMethod, false);
}
+ private DexEncodedMethod synthesizeEqualsMethod() {
+ CfCode cfCode =
+ EnumUnboxingCfMethods.EnumUnboxingMethods_equals(appView.options(), equalsUtilityMethod);
+ return synthesizeUtilityMethod(cfCode, equalsUtilityMethod, false);
+ }
+
+ private DexEncodedMethod synthesizeCompareToMethod() {
+ CfCode cfCode =
+ EnumUnboxingCfMethods.EnumUnboxingMethods_compareTo(
+ appView.options(), compareToUtilityMethod);
+ return synthesizeUtilityMethod(cfCode, compareToUtilityMethod, false);
+ }
+
private DexEncodedMethod synthesizeValuesUtilityMethod() {
CfCode cfCode =
EnumUnboxingCfMethods.EnumUnboxingMethods_values(appView.options(), valuesUtilityMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index a1a6638..cac5801 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -1157,7 +1157,7 @@
nullCheckedBlocks.clear();
for (Instruction user : argument.uniqueUsers()) {
if (user.isAssumeNonNull()) {
- nullCheckedBlocks.add(user.asAssumeNonNull().getBlock());
+ nullCheckedBlocks.add(user.asAssume().getBlock());
}
if (user.isIf()
&& user.asIf().isZeroTest()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
index fa0e683..cdc1793 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Assume;
-import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
@@ -69,7 +68,7 @@
outValue.replaceUsers(specializedOutValue);
// Insert AssumeDynamicType instruction.
- Assume<DynamicTypeAssumption> assumeInstruction =
+ Assume assumeInstruction =
Assume.createAssumeDynamicTypeInstruction(
dynamicUpperBoundType, null, specializedOutValue, outValue, invoke, appView);
assumeInstruction.setPosition(appView.options().debug ? invoke.getPosition() : Position.none());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 499c975..323dd11 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -40,7 +40,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -103,7 +102,7 @@
}
final Map<CandidateInfo, LongLivedProgramMethodSetBuilder<?>> referencedFrom =
- new IdentityHashMap<>();
+ new ConcurrentHashMap<>();
// The map storing all the potential candidates for staticizing.
final ConcurrentHashMap<DexType, CandidateInfo> candidates = new ConcurrentHashMap<>();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index ac915a5..0e0e1c9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -43,6 +43,7 @@
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.BiMap;
@@ -228,7 +229,10 @@
ProgramMethodSet referencedFrom;
if (classStaticizer.referencedFrom.containsKey(info)) {
- referencedFrom = classStaticizer.referencedFrom.remove(info).build(appView);
+ LongLivedProgramMethodSetBuilder<?> referencedFromBuilder =
+ classStaticizer.referencedFrom.remove(info);
+ assert referencedFromBuilder != null;
+ referencedFrom = referencedFromBuilder.build(appView);
materializedReferencedFromCollections.put(info, referencedFrom);
} else {
referencedFrom = ProgramMethodSet.empty();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 53122b0..a01be35 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -767,7 +767,7 @@
}
// If there are aliasing instructions, they should be removed before new-instance.
if (instr.isAssume() && buildersToRemove.contains(instr.outValue().getAliasedValue())) {
- Assume<?> assumeInstruction = instr.asAssume();
+ Assume assumeInstruction = instr.asAssume();
Value src = assumeInstruction.src();
Value dest = assumeInstruction.outValue();
dest.replaceUsers(src);
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
index f9f66e8..61bdbbc 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.synthetic;
+import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
import com.android.tools.r8.cf.code.CfFieldInstruction;
@@ -36,6 +37,49 @@
super(appView, holder);
}
+ public static class EnumUnboxingDefaultToStringCfCodeProvider extends EnumUnboxingCfCodeProvider {
+
+ private DexType enumType;
+ private EnumValueInfoMap map;
+
+ public EnumUnboxingDefaultToStringCfCodeProvider(
+ AppView<?> appView, DexType holder, DexType enumType, EnumValueInfoMap map) {
+ super(appView, holder);
+ this.enumType = enumType;
+ this.map = map;
+ }
+
+ @Override
+ public CfCode generateCfCode() {
+ // Generated static method, for class com.x.MyEnum {A,B} would look like:
+ // String UtilityClass#com.x.MyEnum_toString(int i) {
+ // if (i == 1) { return "A";}
+ // if (i == 2) { return "B";}
+ // throw null;
+ DexItemFactory factory = appView.dexItemFactory();
+ List<CfInstruction> instructions = new ArrayList<>();
+
+ // if (i == 1) { return "A";}
+ // if (i == 2) { return "B";}
+ map.forEach(
+ (field, enumValueInfo) -> {
+ CfLabel dest = new CfLabel();
+ instructions.add(new CfLoad(ValueType.fromDexType(factory.intType), 0));
+ instructions.add(new CfConstNumber(enumValueInfo.convertToInt(), ValueType.INT));
+ instructions.add(new CfIf(If.Type.EQ, ValueType.INT, dest));
+ instructions.add(new CfConstString(field.name));
+ instructions.add(new CfReturn(ValueType.OBJECT));
+ instructions.add(dest);
+ });
+
+ // throw null;
+ instructions.add(new CfConstNull());
+ instructions.add(new CfThrow());
+
+ return standardCfCodeFromInstructions(instructions);
+ }
+ }
+
public static class EnumUnboxingValueOfCfCodeProvider extends EnumUnboxingCfCodeProvider {
private DexType enumType;
diff --git a/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
index e329c4d..b8355c3 100644
--- a/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
+++ b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
@@ -21,7 +21,9 @@
import com.android.tools.r8.shaking.ProguardPathList;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
@@ -292,19 +294,23 @@
}
public RelocatorCommand build() throws CompilationFailedException {
- try {
- if (printHelp || printVersion) {
- return new RelocatorCommand(printHelp, printVersion);
- }
- reporter.failIfPendingErrors();
- validate();
- reporter.failIfPendingErrors();
- DexItemFactory factory = new DexItemFactory();
- return new RelocatorCommand(
- mapping.build(), app.build(), reporter, factory, consumer, threadCount);
- } catch (AbortException e) {
- throw new CompilationFailedException(e);
- }
+ Box<RelocatorCommand> result = new Box<>();
+ ExceptionUtils.withCompilationHandler(
+ reporter,
+ () -> {
+ if (printHelp || printVersion) {
+ result.set(new RelocatorCommand(printHelp, printVersion));
+ return;
+ }
+ reporter.failIfPendingErrors();
+ validate();
+ reporter.failIfPendingErrors();
+ DexItemFactory factory = new DexItemFactory();
+ result.set(
+ new RelocatorCommand(
+ mapping.build(), app.build(), reporter, factory, consumer, threadCount));
+ });
+ return result.get();
}
// Helper to signify an error.
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 6e8f93b..8a54cca 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -242,6 +242,9 @@
/** Set of types that were found to be missing during the first round of tree shaking. */
private Set<DexType> initialMissingTypes;
+ /** Set of types that was pruned during the first round of tree shaking. */
+ private Set<DexType> initialPrunedTypes;
+
/** Mapping from each unused interface to the set of live types that implements the interface. */
private final Map<DexProgramClass, Set<DexProgramClass>> unusedInterfaceTypes =
new IdentityHashMap<>();
@@ -448,6 +451,11 @@
this.initialMissingTypes = initialMissingTypes;
}
+ public void setInitialPrunedTypes(Set<DexType> initialPrunedTypes) {
+ assert mode.isFinalTreeShaking();
+ this.initialPrunedTypes = initialPrunedTypes;
+ }
+
public void addDeadProtoTypeCandidate(DexType type) {
assert type.isProgramType(appView);
addDeadProtoTypeCandidate(appView.definitionFor(type).asProgramClass());
@@ -2878,13 +2886,12 @@
// Verify the references on the pruned application after type synthesis.
assert verifyReferences(app);
+ assert verifyMissingTypes();
AppInfoWithLiveness appInfoWithLiveness =
new AppInfoWithLiveness(
app,
SetUtils.mapIdentityHashSet(deadProtoTypeCandidates, DexProgramClass::getType),
- // TODO(b/155959821): We should be able to assert that missing types is a subset of
- // initialMissingTypes + synthesized types.
mode.isFinalTreeShaking()
? Sets.union(initialMissingTypes, missingTypes)
: missingTypes,
@@ -2959,6 +2966,22 @@
});
}
+ private boolean verifyMissingTypes() {
+ if (initialMissingTypes == null) {
+ assert !mode.isFinalTreeShaking();
+ return true;
+ }
+ missingTypes.forEach(
+ missingType -> {
+ assert initialMissingTypes.contains(missingType)
+ // TODO(b/157107464): See if we can clean this up.
+ || initialPrunedTypes.contains(missingType)
+ || missingType.isD8R8SynthesizedClassType()
+ : missingType;
+ });
+ return true;
+ }
+
private boolean verifyReferences(DexApplication app) {
WorkList<DexClass> worklist = WorkList.newIdentityWorkList();
for (DexProgramClass clazz : liveTypes.getItems()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
index 743f0df..5ff9c97 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
@@ -23,12 +23,14 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
GraphConsumer keptGraphConsumer,
- Set<DexType> initialMissingTypes) {
+ Set<DexType> initialMissingTypes,
+ Set<DexType> initialPrunedTypes) {
Enqueuer enqueuer =
new Enqueuer(appView, subtypingInfo, keptGraphConsumer, Mode.FINAL_TREE_SHAKING);
appView.withProtoShrinker(
shrinker -> enqueuer.setInitialDeadProtoTypes(shrinker.getDeadProtoTypes()));
enqueuer.setInitialMissingTypes(initialMissingTypes);
+ enqueuer.setInitialPrunedTypes(initialPrunedTypes);
return enqueuer;
}
diff --git a/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java b/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java
index 2e34cd7..59a6a14 100644
--- a/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java
@@ -18,7 +18,7 @@
public class CfLineToMethodMapper {
- private final Map<String, Int2ReferenceOpenHashMap<String>> sourceMethodMapping = new HashMap<>();
+ private Map<String, Int2ReferenceOpenHashMap<String>> sourceMethodMapping = null;
private final AndroidApp inputApp;
private static final String NAME_DESCRIPTOR_SEPARATOR = ";;";
@@ -28,7 +28,8 @@
public String lookupNameAndDescriptor(String binaryName, int lineNumber)
throws ResourceException {
- if (sourceMethodMapping.isEmpty()) {
+ if (sourceMethodMapping == null) {
+ sourceMethodMapping = new HashMap<>();
readLineNumbersFromClassFiles();
}
Int2ReferenceOpenHashMap<String> lineMappings = sourceMethodMapping.get(binaryName);
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index e5ba3f1..28d1ed5 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.position.Position;
+import com.google.common.collect.ObjectArrays;
import java.io.IOException;
import java.nio.file.FileSystemException;
import java.nio.file.Paths;
@@ -97,6 +98,10 @@
suppressed.add(innerMostCause);
innerMostCause = innerMostCause.getCause();
}
+ // Add the full stack as a suppressed stack on the inner cause.
+ if (topMostException != innerMostCause) {
+ innerMostCause.addSuppressed(topMostException);
+ }
// If no abort is seen, the exception is not reported, so report it now.
if (!hasBeenReported) {
@@ -116,10 +121,9 @@
new CompilationFailedException(message.toString(), innerMostCause);
// Replace its stack by the cause stack and insert version info at the top.
String filename = "Version_" + Version.LABEL + ".java";
- rethrow.setStackTrace(
- new StackTraceElement[] {
- new StackTraceElement(Version.class.getSimpleName(), "fakeStackEntry", filename, 0)
- });
+ StackTraceElement versionElement =
+ new StackTraceElement(Version.class.getSimpleName(), "fakeStackEntry", filename, 0);
+ rethrow.setStackTrace(ObjectArrays.concat(versionElement, rethrow.getStackTrace()));
return rethrow;
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 43a53c7..8c92acf 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1160,7 +1160,8 @@
|| System.getProperty("com.android.tools.r8.allowTypeErrors") != null;
public boolean allowInvokeErrors = false;
public boolean disableL8AnnotationRemoval = false;
- public boolean allowClassInlinerGracefulExit = true;
+ public boolean allowClassInlinerGracefulExit =
+ System.getProperty("com.android.tools.r8.disallowClassInlinerGracefulExit") == null;
public boolean reportUnusedProguardConfigurationRules = false;
public boolean alwaysUsePessimisticRegisterAllocation = false;
public boolean enableCheckCastAndInstanceOfRemoval = true;
diff --git a/src/main/java/com/android/tools/r8/utils/ObjectUtils.java b/src/main/java/com/android/tools/r8/utils/ObjectUtils.java
new file mode 100644
index 0000000..ac07151
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ObjectUtils.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.util.function.Predicate;
+
+public class ObjectUtils {
+
+ public static <T> boolean getBooleanOrElse(T object, Predicate<T> fn, boolean orElse) {
+ if (object != null) {
+ return fn.test(object);
+ }
+ return orElse;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/Reporter.java b/src/main/java/com/android/tools/r8/utils/Reporter.java
index f7f9d1f..f251b0c 100644
--- a/src/main/java/com/android/tools/r8/utils/Reporter.java
+++ b/src/main/java/com/android/tools/r8/utils/Reporter.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.errors.Unreachable;
public class Reporter implements DiagnosticsHandler {
@@ -56,14 +55,13 @@
*/
public RuntimeException fatalError(Diagnostic error) {
error(error);
- failIfPendingErrors();
- throw new Unreachable();
+ throw abort;
}
/** @throws AbortException if any error was reported. */
public synchronized void failIfPendingErrors() {
if (abort != null) {
- throw abort;
+ throw new RuntimeException(abort);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodMultisetBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodMultisetBuilder.java
new file mode 100644
index 0000000..f3ba06b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodMultisetBuilder.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
+
+public class LongLivedProgramMethodMultisetBuilder {
+
+ private final Multiset<DexMethod> backing = HashMultiset.create();
+
+ private LongLivedProgramMethodMultisetBuilder() {}
+
+ public static LongLivedProgramMethodMultisetBuilder create() {
+ return new LongLivedProgramMethodMultisetBuilder();
+ }
+
+ public void add(ProgramMethod method) {
+ backing.add(method.getReference());
+ }
+
+ public int size() {
+ return backing.size();
+ }
+
+ public ProgramMethodMultiset build(AppView<AppInfoWithLiveness> appView) {
+ ProgramMethodMultiset result = ProgramMethodMultiset.createHash();
+ backing.forEachEntry(
+ (oldMethod, occurrences) -> {
+ DexMethod method = appView.graphLense().getRenamedMethodSignature(oldMethod);
+ DexProgramClass holder = appView.definitionForHolder(method).asProgramClass();
+ result.createAndAdd(holder, holder.lookupMethod(method), occurrences);
+ });
+ return result;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMultiset.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMultiset.java
new file mode 100644
index 0000000..ae8974c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMultiset.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.utils.ProgramMethodEquivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
+import java.util.function.ObjIntConsumer;
+
+public class ProgramMethodMultiset {
+
+ private final Multiset<Wrapper<ProgramMethod>> backing;
+
+ private ProgramMethodMultiset(Multiset<Wrapper<ProgramMethod>> backing) {
+ this.backing = backing;
+ }
+
+ public static ProgramMethodMultiset createHash() {
+ return new ProgramMethodMultiset(HashMultiset.create());
+ }
+
+ public void createAndAdd(DexProgramClass holder, DexEncodedMethod method, int occurrences) {
+ backing.add(wrap(new ProgramMethod(holder, method)), occurrences);
+ }
+
+ public void forEachEntry(ObjIntConsumer<ProgramMethod> consumer) {
+ backing.forEachEntry((wrapper, occurrences) -> consumer.accept(wrapper.get(), occurrences));
+ }
+
+ private static Wrapper<ProgramMethod> wrap(ProgramMethod method) {
+ return ProgramMethodEquivalence.get().wrap(method);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/DXTestBuilder.java b/src/test/java/com/android/tools/r8/DXTestBuilder.java
index e621784..9e26ecd 100644
--- a/src/test/java/com/android/tools/r8/DXTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/DXTestBuilder.java
@@ -29,6 +29,8 @@
// Ordered list of injar entries.
private List<Path> injars = new ArrayList<>();
+ private int minApiLevel = -1;
+
private DXTestBuilder(TestState state) {
super(state, D8Command.builder(), Backend.DEX);
}
@@ -51,6 +53,9 @@
Path outJar = dxOutputFolder.resolve("output.jar");
List<String> args = new ArrayList<>();
+ if (minApiLevel >= 0) {
+ args.add("--min-sdk-version=" + minApiLevel);
+ }
args.add("--output=" + outJar.toString());
args.addAll(injars.stream().map(Path::toString).collect(Collectors.toList()));
ProcessResult result =
@@ -118,4 +123,10 @@
injars.addAll(files);
return self();
}
+
+ @Override
+ public DXTestBuilder setMinApi(int minApiLevel) {
+ this.minApiLevel = minApiLevel;
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index ca9abd4..9a36a04 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -19,6 +19,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.rules.TemporaryFolder;
@@ -57,6 +58,7 @@
private final KotlinTargetVersion targetVersion;
private final List<Path> sources = new ArrayList<>();
private final List<Path> classpath = new ArrayList<>();
+ private final List<String> additionalArguments = new ArrayList<>();
private boolean useJvmAssertions;
private Path output = null;
@@ -84,6 +86,11 @@
return new KotlinCompilerTool(jdk, state, kotlinCompiler, kotlinTargetVersion);
}
+ public KotlinCompilerTool addArguments(String... arguments) {
+ Collections.addAll(additionalArguments, arguments);
+ return this;
+ }
+
public KotlinCompilerTool addSourceFiles(Path... files) {
return addSourceFiles(Arrays.asList(files));
}
@@ -181,6 +188,7 @@
.map(Path::toString)
.collect(Collectors.joining(isWindows() ? ";" : ":")));
}
+ cmdline.addAll(additionalArguments);
ProcessBuilder builder = new ProcessBuilder(cmdline);
return ToolHelper.runProcess(builder);
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 9463a49..ba1d485 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -234,7 +233,7 @@
}
@Override
- public ProguardTestBuilder setMinApi(AndroidApiLevel minApiLevel) {
+ public ProguardTestBuilder setMinApi(int minApiLevel) {
if (backend == Backend.DEX) {
throw new Unimplemented("No support for setting min api");
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 4f8806c..b5c518c 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -120,6 +120,52 @@
}
@Test
+ public void passFeatureSplit() throws Throwable {
+ Path working = temp.getRoot().toPath();
+ Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar").toAbsolutePath();
+ Path inputFeature = Paths.get(EXAMPLES_BUILD_DIR, "arrayaccess.jar").toAbsolutePath();
+ Path library = ToolHelper.getDefaultAndroidJar();
+ Path output = working.resolve("classes.dex");
+ Path featureOutput = working.resolve("feature.zip");
+ assertFalse(Files.exists(output));
+ assertFalse(Files.exists(featureOutput));
+ ProcessResult result =
+ ToolHelper.forkR8(
+ working,
+ input.toString(),
+ "--lib",
+ library.toAbsolutePath().toString(),
+ "--feature",
+ inputFeature.toAbsolutePath().toString(),
+ featureOutput.toAbsolutePath().toString(),
+ "--no-tree-shaking");
+ assertEquals("R8 run failed: " + result.stderr, 0, result.exitCode);
+ assertTrue(Files.exists(output));
+ assertTrue(Files.exists(featureOutput));
+ }
+
+ @Test
+ public void featureOnlyOneArgument() throws Throwable {
+ Path working = temp.getRoot().toPath();
+ Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar").toAbsolutePath();
+ Path inputFeature = Paths.get(EXAMPLES_BUILD_DIR, "arrayaccess.jar").toAbsolutePath();
+ Path library = ToolHelper.getDefaultAndroidJar();
+ Path output = working.resolve("classes.dex");
+ assertFalse(Files.exists(output));
+ ProcessResult result =
+ ToolHelper.forkR8(
+ working,
+ input.toString(),
+ "--lib",
+ library.toAbsolutePath().toString(),
+ "--no-tree-shaking",
+ "--feature",
+ inputFeature.toAbsolutePath().toString());
+ assertNotEquals("R8 run failed: " + result.stderr, 0, result.exitCode);
+ assertTrue(result.stderr.contains("Missing parameter for"));
+ }
+
+ @Test
public void flagsFile() throws Throwable {
Path working = temp.getRoot().toPath();
Path library = ToolHelper.getDefaultAndroidJar();
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 8ae9c0f..b7869e4 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -215,17 +215,16 @@
}
public T setMinApi(AndroidApiLevel minApiLevel) {
- assert builder.getMinApiLevel() > 0 || this.defaultMinApiLevel != null
- : "Tests must use this method to set min API level, and not"
- + " BaseCompilerCommand.Builder.setMinApiLevel()";
if (backend == Backend.DEX) {
- this.defaultMinApiLevel = null;
- builder.setMinApiLevel(minApiLevel.getLevel());
+ return setMinApi(minApiLevel.getLevel());
}
return self();
}
public T setMinApi(int minApiLevel) {
+ assert builder.getMinApiLevel() > 0 || this.defaultMinApiLevel != null
+ : "Tests must use this method to set min API level, and not"
+ + " BaseCompilerCommand.Builder.setMinApiLevel()";
if (backend == Backend.DEX) {
this.defaultMinApiLevel = null;
builder.setMinApiLevel(minApiLevel);
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index 0cab77c..ac3ef82 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -7,6 +7,8 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static org.hamcrest.CoreMatchers.not;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import org.hamcrest.Matcher;
@@ -34,13 +36,29 @@
// Match exact.
- TestDiagnosticMessages assertDiagnosticsMatch(Matcher<Diagnostic>... matchers);
+ default TestDiagnosticMessages assertDiagnosticsMatch(Matcher matcher) {
+ return assertDiagnosticsMatch(Collections.singletonList(matcher));
+ }
- TestDiagnosticMessages assertInfosMatch(Matcher<Diagnostic>... matchers);
+ TestDiagnosticMessages assertDiagnosticsMatch(Collection<Matcher<Diagnostic>> matchers);
- TestDiagnosticMessages assertWarningsMatch(Matcher<Diagnostic>... matchers);
+ default TestDiagnosticMessages assertInfosMatch(Matcher<Diagnostic> matcher) {
+ return assertInfosMatch(Collections.singletonList(matcher));
+ }
- TestDiagnosticMessages assertErrorsMatch(Matcher<Diagnostic>... matchers);
+ TestDiagnosticMessages assertInfosMatch(Collection<Matcher<Diagnostic>> matchers);
+
+ default TestDiagnosticMessages assertWarningsMatch(Matcher<Diagnostic> matcher) {
+ return assertWarningsMatch(Collections.singletonList(matcher));
+ }
+
+ TestDiagnosticMessages assertWarningsMatch(Collection<Matcher<Diagnostic>> matchers);
+
+ default TestDiagnosticMessages assertErrorsMatch(Matcher<Diagnostic> matcher) {
+ return assertErrorsMatch(Collections.singletonList(matcher));
+ }
+
+ TestDiagnosticMessages assertErrorsMatch(Collection<Matcher<Diagnostic>> matchers);
// Match one.
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index 2635732..19a700d 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -174,7 +174,7 @@
}
private static void assertDiagnosticsMatch(
- Iterable<Diagnostic> diagnostics, String tag, List<Matcher<Diagnostic>> matchers) {
+ Iterable<Diagnostic> diagnostics, String tag, Collection<Matcher<Diagnostic>> matchers) {
// Match is unordered, but we make no attempts to find the maximum match.
int diagnosticsCount = 0;
Set<Diagnostic> matchedDiagnostics = new HashSet<>();
@@ -236,26 +236,26 @@
}
@Override
- public TestDiagnosticMessages assertDiagnosticsMatch(Matcher<Diagnostic>... matchers) {
- assertDiagnosticsMatch(getAllDiagnostics(), "diagnostics", Arrays.asList(matchers));
+ public TestDiagnosticMessages assertDiagnosticsMatch(Collection<Matcher<Diagnostic>> matchers) {
+ assertDiagnosticsMatch(getAllDiagnostics(), "diagnostics", matchers);
return this;
}
@Override
- public TestDiagnosticMessages assertInfosMatch(Matcher<Diagnostic>... matchers) {
- assertDiagnosticsMatch(getInfos(), "infos", Arrays.asList(matchers));
+ public TestDiagnosticMessages assertInfosMatch(Collection<Matcher<Diagnostic>> matchers) {
+ assertDiagnosticsMatch(getInfos(), "infos", matchers);
return this;
}
@Override
- public TestDiagnosticMessages assertWarningsMatch(Matcher<Diagnostic>... matchers) {
- assertDiagnosticsMatch(getWarnings(), "warnings", Arrays.asList(matchers));
+ public TestDiagnosticMessages assertWarningsMatch(Collection<Matcher<Diagnostic>> matchers) {
+ assertDiagnosticsMatch(getWarnings(), "warnings", matchers);
return this;
}
@Override
- public TestDiagnosticMessages assertErrorsMatch(Matcher<Diagnostic>... matchers) {
- assertDiagnosticsMatch(getErrors(), "errors", Arrays.asList(matchers));
+ public TestDiagnosticMessages assertErrorsMatch(Collection<Matcher<Diagnostic>> matchers) {
+ assertDiagnosticsMatch(getErrors(), "errors", matchers);
return this;
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index f19c7ce..3d00718 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -229,17 +229,16 @@
ART_7_0_0_HOST(Version.V7_0_0, Kind.HOST),
ART_8_1_0_TARGET(Version.V8_1_0, Kind.TARGET),
ART_8_1_0_HOST(Version.V8_1_0, Kind.HOST),
+ ART_DEFAULT(Version.DEFAULT, Kind.HOST),
ART_9_0_0_TARGET(Version.V9_0_0, Kind.TARGET),
ART_9_0_0_HOST(Version.V9_0_0, Kind.HOST),
ART_10_0_0_TARGET(Version.V10_0_0, Kind.TARGET),
- ART_10_0_0_HOST(Version.V10_0_0, Kind.HOST),
- ART_DEFAULT(Version.DEFAULT, Kind.HOST);
+ ART_10_0_0_HOST(Version.V10_0_0, Kind.HOST);
private static final ImmutableMap<String, DexVm> SHORT_NAME_MAP =
Arrays.stream(DexVm.values()).collect(ImmutableMap.toImmutableMap(
DexVm::toString, Function.identity()));
-
public enum Version {
V4_0_4("4.0.4"),
V4_4_4("4.4.4"),
@@ -247,9 +246,9 @@
V6_0_1("6.0.1"),
V7_0_0("7.0.0"),
V8_1_0("8.1.0"),
+ DEFAULT("default"),
V9_0_0("9.0.0"),
- V10_0_0("10.0.0"),
- DEFAULT("default");
+ V10_0_0("10.0.0");
Version(String shortName) {
this.shortName = shortName;
@@ -260,7 +259,7 @@
}
public boolean isLatest() {
- return this == DEFAULT;
+ return this == V10_0_0;
}
public boolean isNewerThan(Version other) {
@@ -286,7 +285,7 @@
}
public static Version last() {
- return DEFAULT;
+ return V10_0_0;
}
static {
@@ -896,6 +895,7 @@
return dexVm == DexVm.ART_DEFAULT;
}
+ @Deprecated
public static DexVm getDexVm() {
String artVersion = System.getProperty("dex_vm");
if (artVersion == null) {
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index eca0441..069a504 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -90,10 +90,6 @@
return dexVmVersion.isAtLeast(Version.V7_0_0);
}
- public static boolean fromAndroidO(Version dexVmVersion) {
- return dexVmVersion.isAtLeast(Version.DEFAULT);
- }
-
private static List<Path> findAllJarsIn(Path root) {
try {
return Files.walk(root)
diff --git a/src/test/java/com/android/tools/r8/desugar/b72538146/B72538146.java b/src/test/java/com/android/tools/r8/desugar/b72538146/B72538146.java
index bfc400d..fbb29e8 100644
--- a/src/test/java/com/android/tools/r8/desugar/b72538146/B72538146.java
+++ b/src/test/java/com/android/tools/r8/desugar/b72538146/B72538146.java
@@ -4,47 +4,73 @@
package com.android.tools.r8.desugar.b72538146;
-import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.OutputMode;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.VmTestRunner;
-import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderOrEqualThan;
-import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
-@RunWith(VmTestRunner.class)
+@RunWith(Parameterized.class)
public class B72538146 extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+ .withAllApiLevels()
+ .build();
+ }
+
+ public B72538146(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
@Test
- @IgnoreIfVmOlderOrEqualThan(Version.V6_0_1)
public void test() throws Exception {
- // Build the main app from source compiled separately using the Android API for classloading.
- AndroidApp.Builder builder = AndroidApp.builder();
- builder.addProgramFile(
- Paths.get("build/test/examplesAndroidApi/classes/classloader/Runner.class"));
- AndroidApp app = compileWithD8(builder.build());
-
// Compile the parent and child applications into separate dex applications.
- Path parent = temp.newFolder("parent").toPath().resolve("classes.zip");
- Path child = temp.newFolder("child").toPath().resolve("classes.zip");
- AndroidApp parentApp = readClasses(
- Parent.class,
- Parent.Inner1.class,
- Parent.Inner2.class,
- Parent.Inner3.class,
- Parent.Inner4.class);
- compileWithD8(parentApp).write(parent, OutputMode.DexIndexed);
+ List<Class<?>> parentClasses =
+ ImmutableList.of(
+ Parent.class,
+ Parent.Inner1.class,
+ Parent.Inner2.class,
+ Parent.Inner3.class,
+ Parent.Inner4.class);
- AndroidApp childApp = readClasses(Child.class);
- compileWithD8(childApp).write(child, OutputMode.DexIndexed);
+ Path parent =
+ testForD8()
+ .addProgramClasses(parentClasses)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
+
+ Path child =
+ testForD8()
+ .addProgramClasses(Child.class)
+ .addClasspathClasses(parentClasses)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
// Run the classloader test loading the two dex applications.
- String result = runOnArt(app, "classloader.Runner",
- parent.toString(), child.toString(), "com.android.tools.r8.desugar.b72538146.Child");
- assertEquals("SUCCESS", result);
+ testForD8()
+ .addProgramFiles(
+ Paths.get("build/test/examplesAndroidApi/classes/classloader/Runner.class"))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(
+ parameters.getRuntime(),
+ "classloader.Runner",
+ parent.toString(),
+ child.toString(),
+ Child.class.getTypeName())
+ .assertSuccessWithOutput("SUCCESS");
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
index 6fdfe97..03e7f70 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
@@ -40,6 +40,7 @@
public void testR8CompiledWithD8() throws Exception {
testForD8()
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
+ .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
.compile()
.inspect(Java11D8CompilationTest::assertNoNests);
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
index 7151dfa..32cfb18 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -138,7 +138,7 @@
}
@Test(expected = CompilationFailedException.class)
- @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.DEFAULT) // No desugaring
+ @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.V10_0_0) // No desugaring
public void testInvokeDefault1() throws Exception {
ensureSameOutput(
TestMainDefault1.class.getCanonicalName(),
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java b/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
index 1f19d89..b9eabe6 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
@@ -28,6 +28,7 @@
import java.io.StringWriter;
import java.util.concurrent.atomic.AtomicBoolean;
import org.hamcrest.Matcher;
+import org.hamcrest.core.IsAnything;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -35,8 +36,6 @@
@RunWith(Parameterized.class)
public class ErrorDuringIrConversionTest extends TestBase {
- static final String EXPECTED = StringUtils.lines("Hello, world");
-
static final Origin ORIGIN =
new Origin(Origin.root()) {
@Override
@@ -61,10 +60,21 @@
CompilationFailedException e, Matcher<String> messageMatcher, Matcher<String> stackMatcher) {
// Check that the failure exception exiting the compiler contains origin info in the message.
assertThat(e.getMessage(), messageMatcher);
- // Check that the stack trace has the version marker.
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
- assertThat(writer.toString(), stackMatcher);
+ String fullStackTrace = writer.toString();
+ // Extract the top cause stack.
+ int topStackTraceEnd = fullStackTrace.indexOf("Caused by:");
+ String topStackTrace = fullStackTrace.substring(0, topStackTraceEnd);
+ String restStackTrace = fullStackTrace.substring(topStackTraceEnd);
+ // Check that top stack trace always has the version marker.
+ assertThat(topStackTrace, containsString("fakeStackEntry"));
+ // Check that top stack has the D8 entry (from tests the non-renamed entry is ToolHelper.runD8).
+ assertThat(topStackTrace, containsString("com.android.tools.r8.ToolHelper.runD8("));
+ // Check that the stack trace always has the suppressed info.
+ assertThat(restStackTrace, containsString(StringUtils.LINE_SEPARATOR + "\tSuppressed:"));
+ // Custom test checks.
+ assertThat(restStackTrace, stackMatcher);
}
private static void throwNPE() {
@@ -90,9 +100,7 @@
});
} catch (CompilationFailedException e) {
checkCompilationFailedException(
- e,
- containsString(ORIGIN.toString()),
- allOf(containsString("fakeStackEntry"), containsString("throwNPE")));
+ e, containsString(ORIGIN.toString()), containsString("throwNPE"));
return;
}
fail("Expected compilation to fail");
@@ -122,8 +130,7 @@
diagnosticOrigin(Origin.unknown())));
});
} catch (CompilationFailedException e) {
- checkCompilationFailedException(
- e, containsString(ORIGIN.toString()), containsString("fakeStackEntry"));
+ checkCompilationFailedException(e, containsString(ORIGIN.toString()), new IsAnything<>());
return;
}
fail("Expected compilation to fail");
@@ -173,10 +180,9 @@
// There may be no fail-if-error barrier inside any origin association, thus only the
// top level message can be expected here.
containsString("Compilation failed to complete"),
- // The stack trace must contain both the version, the frame for the hook above, and one
+ // The stack trace must contain the reportErrors frame for the hook above, and one
// of the error messages.
allOf(
- containsString("fakeStackEntry"),
containsString("reportErrors"),
anyOf(containsString("FOO!"), containsString("BAR!"), containsString("BAZ!"))));
return;
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
new file mode 100644
index 0000000..de86bd7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+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 EqualsCompareToEnumUnboxingTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final KeepRule enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public EqualsCompareToEnumUnboxingTest(
+ TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ Class<?> success = EnumEqualscompareTo.class;
+ R8TestRunResult run =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(EqualsCompareToEnumUnboxingTest.class)
+ .addKeepMainRule(EnumEqualscompareTo.class)
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRule())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .allowDiagnosticInfoMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ m ->
+ assertEnumIsUnboxed(
+ success.getDeclaredClasses()[0], success.getSimpleName(), m))
+ .run(parameters.getRuntime(), success)
+ .assertSuccess();
+ assertLines2By2Correct(run.getStdOut());
+ }
+
+ static class EnumEqualscompareTo {
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B
+ }
+
+ public static void main(String[] args) {
+ equalsTest();
+ compareToTest();
+ }
+
+ @SuppressWarnings({"ConstantConditions", "EqualsWithItself", "ResultOfMethodCallIgnored"})
+ private static void equalsTest() {
+ System.out.println(MyEnum.A.equals(MyEnum.B));
+ System.out.println(false);
+ System.out.println(MyEnum.A.equals(MyEnum.A));
+ System.out.println(true);
+ System.out.println(MyEnum.A.equals(null));
+ System.out.println(false);
+ try {
+ ((MyEnum) null).equals(null);
+ } catch (NullPointerException npe) {
+ System.out.println("npe " + npe.getMessage());
+ System.out.println("npe " + npe.getMessage());
+ }
+ }
+
+ @SuppressWarnings({"ConstantConditions", "EqualsWithItself", "ResultOfMethodCallIgnored"})
+ private static void compareToTest() {
+ System.out.println(MyEnum.B.compareTo(MyEnum.A) > 0);
+ System.out.println(true);
+ System.out.println(MyEnum.A.compareTo(MyEnum.B) < 0);
+ System.out.println(true);
+ System.out.println(MyEnum.A.compareTo(MyEnum.A) == 0);
+ System.out.println(true);
+ try {
+ ((MyEnum) null).equals(null);
+ } catch (NullPointerException npe) {
+ System.out.println("npe " + npe.getMessage());
+ System.out.println("npe " + npe.getMessage());
+ }
+ try {
+ MyEnum.A.compareTo(null);
+ } catch (NullPointerException npe) {
+ System.out.println("npe " + npe.getMessage());
+ System.out.println("npe " + npe.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java
new file mode 100644
index 0000000..d8d781e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ToStringEnumUnboxingTest.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+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 ToStringEnumUnboxingTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final KeepRule enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public ToStringEnumUnboxingTest(
+ TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ Class<?> success = EnumNameToString.class;
+ R8TestRunResult run =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ToStringEnumUnboxingTest.class)
+ .addKeepMainRule(EnumNameToString.class)
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRule())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .allowDiagnosticInfoMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ m ->
+ assertEnumIsUnboxed(
+ success.getDeclaredClasses()[0], success.getSimpleName(), m))
+ .run(parameters.getRuntime(), success)
+ .assertSuccess();
+ assertLines2By2Correct(run.getStdOut());
+ }
+
+ static class EnumNameToString {
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ public static void main(String[] args) {
+ System.out.println(MyEnum.A.toString());
+ System.out.println(MyEnum.A.name());
+ System.out.println(MyEnum.B.toString());
+ System.out.println(MyEnum.B.name());
+ try {
+ System.out.println(((MyEnum) null).toString());
+ } catch (NullPointerException e) {
+ System.out.println("npeToString " + e.getMessage());
+ System.out.println("npeToString " + e.getMessage());
+ }
+ try {
+ System.out.println(((MyEnum) null).name());
+ } catch (NullPointerException e) {
+ System.out.println("npeName " + e.getMessage());
+ System.out.println("npeName " + e.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java
new file mode 100644
index 0000000..b7643a0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ToStringOverrideEnumUnboxingTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+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 ToStringOverrideEnumUnboxingTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final KeepRule enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public ToStringOverrideEnumUnboxingTest(
+ TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ Class<?> success = EnumNameToString.class;
+ R8TestRunResult run =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ToStringOverrideEnumUnboxingTest.class)
+ .addKeepMainRule(EnumNameToString.class)
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRule())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .allowDiagnosticInfoMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ m -> assertEnumIsBoxed(success.getDeclaredClasses()[0], success.getSimpleName(), m))
+ .run(parameters.getRuntime(), success)
+ .assertSuccess();
+ assertLines2By2Correct(run.getStdOut());
+ }
+
+ static class EnumNameToString {
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B {
+ @Override
+ public String toString() {
+ return "bezinga";
+ }
+ }
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ public static void main(String[] args) {
+ System.out.println(MyEnum.A.toString());
+ System.out.println(MyEnum.A.name());
+ System.out.println(MyEnum.B.toString());
+ System.out.println("bezinga");
+ System.out.println(MyEnum.B.name());
+ System.out.println("B");
+ try {
+ System.out.println(((MyEnum) null).toString());
+ } catch (NullPointerException e) {
+ System.out.println("npeToString " + e.getMessage());
+ System.out.println("npeToString " + e.getMessage());
+ }
+ try {
+ System.out.println(((MyEnum) null).name());
+ } catch (NullPointerException e) {
+ System.out.println("npeName " + e.getMessage());
+ System.out.println("npeName " + e.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java
index 6dbb356..c8ea9ec 100644
--- a/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.VmTestRunner;
import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderThan;
@@ -24,6 +25,7 @@
runAndCheckVerification(
D8Command.builder()
.addProgramFiles(Paths.get(JAR))
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
.setMode(CompilationMode.DEBUG)
.setMinApiLevel(MIN_SDK),
JAR);
@@ -35,6 +37,7 @@
runAndCheckVerification(
D8Command.builder()
.addProgramFiles(Paths.get(JAR))
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
.setMode(CompilationMode.RELEASE)
.setMinApiLevel(MIN_SDK),
JAR);
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index ce6df5c..06f2813 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -24,7 +24,7 @@
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.NonNullTracker;
+import com.android.tools.r8.ir.optimize.AssumeInserter;
import com.android.tools.r8.ir.optimize.NonNullTrackerTestBase;
import com.android.tools.r8.ir.optimize.nonnull.FieldAccessTest;
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterArrayAccess;
@@ -51,7 +51,7 @@
CodeInspector codeInspector = new CodeInspector(appView.appInfo().app());
MethodSubject fooSubject = codeInspector.clazz(mainClass.getName()).method(signature);
IRCode irCode = fooSubject.buildIR();
- new NonNullTracker(appView).insertAssumeInstructions(irCode, Timing.empty());
+ new AssumeInserter(appView).insertAssumeInstructions(irCode, Timing.empty());
inspector.accept(appView, irCode);
verifyLastInvoke(irCode, npeCaught);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index 06305cf..dac8b92 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -40,9 +40,9 @@
IRCode code = fooSubject.buildIR();
checkCountOfNonNull(code, 0);
- NonNullTracker nonNullTracker = new NonNullTracker(appView);
+ AssumeInserter assumeInserter = new AssumeInserter(appView);
- nonNullTracker.insertAssumeInstructions(code, Timing.empty());
+ assumeInserter.insertAssumeInstructions(code, Timing.empty());
assertTrue(code.isConsistentSSA());
checkCountOfNonNull(code, expectedNumberOfNonNull);
@@ -69,8 +69,7 @@
|| (prev.isIf() && prev.asIf().isZeroTest())
|| !curr.getBlock().getPredecessors().contains(prev.getBlock()));
// Make sure non-null is used or inserted for arguments.
- assertTrue(
- curr.outValue().numberOfAllUsers() > 0 || curr.asAssumeNonNull().src().isArgument());
+ assertTrue(curr.outValue().numberOfAllUsers() > 0 || curr.asAssume().src().isArgument());
count++;
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 8ff71b8..75c45d1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -374,7 +374,7 @@
.getMethod()
.name
.toString()
- .equals(EnumUnboxingRewriter.ENUM_UNBOXING_UTILITY_ORDINAL)) {
+ .startsWith(EnumUnboxingRewriter.ENUM_UNBOXING_UTILITY_METHOD_PREFIX)) {
++invokeCount;
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
index d55906a..438aa81 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
@@ -82,7 +82,7 @@
.compile()
.inspect(this::checkOutlineFromFeature);
- // Check that parts of method1 and method2 in FeatureClass was outlined.
+ // Check that parts of method1, ..., method4 in FeatureClass was outlined.
ClassSubject featureClass = compileResult.inspector().clazz(FeatureClass.class);
assertThat(featureClass, isPresent());
String outlineClassName =
@@ -92,8 +92,10 @@
.get(OutlineOptions.CLASS_NAME);
assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method1"), outlineClassName));
assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method2"), outlineClassName));
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method3"), outlineClassName));
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method4"), outlineClassName));
- compileResult.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput("1234");
+ compileResult.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput("123456");
}
private void checkNoOutlineFromFeature(CodeInspector inspector) {
@@ -106,6 +108,7 @@
@Test
public void testWithSplit() throws Exception {
Path featureCode = temp.newFile("feature.zip").toPath();
+
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class, FeatureAPI.class)
@@ -118,7 +121,7 @@
.compile()
.inspect(this::checkNoOutlineFromFeature);
- // Check that parts of method1 and method2 in FeatureClass was not outlined.
+ // Check that parts of method1, ..., method4 in FeatureClass was not outlined.
CodeInspector featureInspector = new CodeInspector(featureCode);
ClassSubject featureClass = featureInspector.clazz(FeatureClass.class);
assertThat(featureClass, isPresent());
@@ -129,6 +132,8 @@
.get(OutlineOptions.CLASS_NAME);
assertFalse(invokesOutline(featureClass.uniqueMethodWithName("method1"), outlineClassName));
assertFalse(invokesOutline(featureClass.uniqueMethodWithName("method2"), outlineClassName));
+ assertFalse(invokesOutline(featureClass.uniqueMethodWithName("method3"), outlineClassName));
+ assertFalse(invokesOutline(featureClass.uniqueMethodWithName("method4"), outlineClassName));
// Run the code without the feature code present.
compileResult.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput("12");
@@ -137,7 +142,7 @@
compileResult
.addRunClasspathFiles(featureCode)
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput("1234");
+ .assertSuccessWithOutput("123456");
}
public static class TestClass {
@@ -193,14 +198,23 @@
}
public static void feature(int i) {
- FeatureClass.method1(new FeatureClass(i), new FeatureClass(i + 1));
+ method1(i, i + 1);
+ method3(new FeatureClass(i + 2), new FeatureClass(i + 3));
}
- public static void method1(FeatureClass fc1, FeatureClass fc2) {
+ public static void method1(int i1, int i2) {
+ System.out.print(i1 + "" + i2);
+ }
+
+ public static void method2(int i1, int i2) {
+ System.out.print(i1 + "" + i2);
+ }
+
+ public static void method3(FeatureClass fc1, FeatureClass fc2) {
System.out.print(fc1.getI() + "" + fc2.getI());
}
- public static void method2(FeatureClass fc1, FeatureClass fc2) {
+ public static void method4(FeatureClass fc1, FeatureClass fc2) {
System.out.print(fc1.getI() + "" + fc2.getI());
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
new file mode 100644
index 0000000..904f69f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.coroutines;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KotlinxCoroutinesTestRunner extends KotlinTestBase {
+
+ private static final String PKG = "kotlinx-coroutines-1.3.6";
+ private static final Path BASE_LIBRARY =
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, PKG, "deps_all-1.3.6-SNAPSHOT.jar");
+ private static final Path TEST_SOURCES =
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, PKG, "kotlinx-coroutines-test-test-sources");
+ private static final List<Path> DEPENDENCIES =
+ ImmutableList.of(
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, PKG, "atomicfu-0.14.3.jar"),
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, PKG, "hamcrest-core-1.3.jar"),
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, PKG, "junit-4.13.jar"),
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, PKG, "kotlin-test-1.3.72.jar"),
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, PKG, "kotlin-test-junit-1.3.71.jar"),
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, PKG, "kotlinx.coroutines.testbase.jar"),
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, PKG, "kotlinx.coroutines.test.main.jar"));
+
+ // Tests that do not run correctly in general - that is these tests are expected to fail always.
+ private Set<String> notWorkingTests =
+ Sets.newHashSet("kotlinx.coroutines.test.TestDispatchersTest");
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ private final TestParameters parameters;
+
+ public KotlinxCoroutinesTestRunner(TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void runKotlinxCoroutinesTests_smoke() throws Exception {
+ Path baseJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addArguments(
+ "-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi",
+ "-Xuse-experimental=kotlinx.coroutines.ObsoleteCoroutinesApi",
+ "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi")
+ .addClasspathFiles(DEPENDENCIES)
+ .addClasspathFiles(BASE_LIBRARY)
+ .addSourceFiles(TEST_SOURCES)
+ .compile();
+ runTestsInJar(baseJar, BASE_LIBRARY);
+ }
+
+ private void runTestsInJar(Path testJar, Path deps) throws Exception {
+ List<Path> dependencies = new ArrayList<>(DEPENDENCIES);
+ dependencies.add(deps);
+ dependencies.add(testJar);
+ ZipUtils.iter(
+ testJar.toString(),
+ (entry, input) -> {
+ if (!entry.isDirectory() && entry.getName().endsWith("Test.class")) {
+ runTest(dependencies, entry.getName());
+ }
+ });
+ }
+
+ private void runTest(List<Path> dependencies, String name) throws IOException {
+ String testName = name.replace("/", ".").replace(".class", "");
+ if (notWorkingTests.contains(testName)) {
+ return;
+ }
+ ProcessResult processResult =
+ ToolHelper.runJava(
+ parameters.getRuntime().asCf(), dependencies, "org.junit.runner.JUnitCore", testName);
+ assertEquals(0, processResult.exitCode);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java b/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java
index eb670d6..d420618 100644
--- a/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java
+++ b/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java
@@ -52,7 +52,7 @@
try {
SeedMapper.seedMapperFromFile(reporter, applyMappingFile);
fail("Should have thrown an error");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
assertEquals(1, testDiagnosticMessages.getErrors().size());
Diagnostic diagnostic = testDiagnosticMessages.getErrors().get(0);
assertEquals(
@@ -75,7 +75,7 @@
try {
SeedMapper.seedMapperFromFile(reporter, applyMappingFile);
fail("Should have thrown an error");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
assertEquals(1, testDiagnosticMessages.getErrors().size());
Diagnostic diagnostic = testDiagnosticMessages.getErrors().get(0);
assertEquals(
@@ -98,7 +98,7 @@
try {
SeedMapper.seedMapperFromFile(reporter, applyMappingFile);
fail("Should have thrown an error");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
assertEquals(1, testDiagnosticMessages.getErrors().size());
Diagnostic diagnostic = testDiagnosticMessages.getErrors().get(0);
assertEquals(
@@ -116,7 +116,7 @@
try {
SeedMapper.seedMapperFromFile(reporter, applyMappingFile);
fail("Should have thrown an error");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
assertEquals(1, testDiagnosticMessages.getErrors().size());
Diagnostic diagnostic = testDiagnosticMessages.getErrors().get(0);
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
index 5d06942..cc558a1 100644
--- a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
+++ b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
@@ -11,13 +11,14 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.code.AddIntLit8;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import dalvik.annotation.optimization.ReachabilitySensitive;
@@ -26,7 +27,6 @@
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
-import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -68,40 +68,48 @@
@RunWith(Parameterized.class)
public class ReachabilitySensitiveTest extends TestBase {
+ private final TestParameters parameters;
private final Tool tool;
- @Parameters(name = "{0}")
- public static List<Object> data() {
- return ImmutableList.of(Tool.D8, Tool. R8);
+ @Parameters(name = "{0} tool: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ ImmutableList.of(Tool.D8, Tool.R8));
}
- public ReachabilitySensitiveTest(Tool tool) {
+ public ReachabilitySensitiveTest(TestParameters parameters, Tool tool) {
+ this.parameters = parameters;
this.tool = tool;
}
+ private int getNumRegisters() {
+ // With API level >= Q we are allowed to re-use the receiver's register.
+ // See also InternalOptions.canHaveThisJitCodeDebuggingBug().
+ assert parameters.isDexRuntime();
+ return parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.Q) ? 2 : 3;
+ }
+
@Test
public void testNoAnnotation()
throws IOException, CompilationFailedException, ExecutionException, NoSuchMethodException {
- CodeInspector inspector = tool == Tool.R8
- ? compileR8(TestClass.class)
- : compile(TestClass.class);
+ CodeInspector inspector =
+ tool == Tool.R8 ? compileR8(TestClass.class) : compile(TestClass.class);
DexCode code =
inspector.method(TestClass.class.getMethod("method")).getMethod().getCode().asDexCode();
// Computation of k is constant folded and the value takes up one register. System.out takes
- // up another register and the receiver is the last.
- Assume.assumeTrue(
- "TODO(b/144966342): Why 2 on Q?",
- ToolHelper.getDexVm().isOlderThanOrEqual(ToolHelper.DexVm.ART_9_0_0_HOST));
- assertEquals(3, code.registerSize);
+ // up another register and the receiver is the last, unless on Q+.
+ assertEquals(getNumRegisters(), code.registerSize);
checkNoLocals(code);
}
@Test
public void testFieldAnnotation()
throws IOException, CompilationFailedException, ExecutionException, NoSuchMethodException {
- CodeInspector inspector = tool == Tool.R8
- ? compileR8(TestClassWithAnnotatedField.class)
- : compile(TestClassWithAnnotatedField.class);
+ CodeInspector inspector =
+ tool == Tool.R8
+ ? compileR8(TestClassWithAnnotatedField.class)
+ : compile(TestClassWithAnnotatedField.class);
checkAnnotatedCode(
inspector
.method(TestClassWithAnnotatedField.class.getMethod("method"))
@@ -113,9 +121,10 @@
@Test
public void testMethodAnnotation()
throws IOException, CompilationFailedException, ExecutionException, NoSuchMethodException {
- CodeInspector inspector = tool == Tool.R8
- ? compileR8(TestClassWithAnnotatedMethod.class)
- : compile(TestClassWithAnnotatedMethod.class);
+ CodeInspector inspector =
+ tool == Tool.R8
+ ? compileR8(TestClassWithAnnotatedMethod.class)
+ : compile(TestClassWithAnnotatedMethod.class);
checkAnnotatedCode(
inspector
.method(TestClassWithAnnotatedMethod.class.getMethod("method"))
@@ -127,9 +136,10 @@
private void checkNoLocals(DexCode code) {
// Even if we preserve live range of locals, we do not output locals information
// as this is a release build.
- assertTrue((code.getDebugInfo() == null) ||
- Arrays.stream(code.getDebugInfo().events)
- .allMatch(event -> !(event instanceof StartLocal)));
+ assertTrue(
+ (code.getDebugInfo() == null)
+ || Arrays.stream(code.getDebugInfo().events)
+ .allMatch(event -> !(event instanceof StartLocal)));
}
private void checkAnnotatedCode(DexCode code) {
@@ -155,6 +165,7 @@
throws CompilationFailedException, IOException, ExecutionException {
return testForD8()
.addProgramClasses(classes)
+ .setMinApi(parameters.getApiLevel())
.setMode(CompilationMode.RELEASE)
.compile()
.inspector();
@@ -170,6 +181,7 @@
.addProgramClasses(classes)
// TODO(ager): This will be in android.jar over time. For now, make it part of the app.
.addProgramClasses(ReachabilitySensitive.class)
+ .setMinApi(parameters.getApiLevel())
.setMode(CompilationMode.RELEASE)
// Keep the input class and its methods.
.addKeepRules(keepRules)
diff --git a/src/test/java/com/android/tools/r8/regress/b120164595/B120164595.java b/src/test/java/com/android/tools/r8/regress/b120164595/B120164595.java
index 7e56259..e63bcbb 100644
--- a/src/test/java/com/android/tools/r8/regress/b120164595/B120164595.java
+++ b/src/test/java/com/android/tools/r8/regress/b120164595/B120164595.java
@@ -69,7 +69,7 @@
builder -> {
builder.appendArtOption("-Xusejit:true");
},
- DexVm.ART_10_0_0_HOST);
+ DexVm.fromVersion(DexVm.Version.last()));
assertEquals(0, artResult.exitCode);
assertFalse(artResult.stderr.contains("Expected NullPointerException"));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index 2e123d7..ecb398a 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -16,20 +16,17 @@
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.jasmin.JasminTestBase;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.Collection;
import org.hamcrest.Matcher;
-import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -48,25 +45,109 @@
}
private enum Mode {
- NO_INVOKE,
- INVOKE_UNVERIFIABLE_METHOD,
- INVOKE_VERIFIABLE_METHOD_ON_UNVERIFIABLE_CLASS;
+ NO_INVOKE {
- public String instruction() {
- switch (this) {
- case NO_INVOKE:
- return "";
-
- case INVOKE_UNVERIFIABLE_METHOD:
- return "invokestatic UnverifiableClass/unverifiableMethod()V";
-
- case INVOKE_VERIFIABLE_METHOD_ON_UNVERIFIABLE_CLASS:
- return "invokestatic UnverifiableClass/verifiableMethod()V";
-
- default:
- throw new Unreachable();
+ @Override
+ public String getExpectedOutput(
+ Compiler compiler, TestRuntime runtime, boolean useInterface) {
+ return StringUtils.joinLines("Hello!", "Goodbye!", "");
}
- }
+
+ @Override
+ public String instruction() {
+ return "";
+ }
+ },
+ INVOKE_UNVERIFIABLE_METHOD {
+
+ @Override
+ public String getExpectedOutput(
+ Compiler compiler, TestRuntime runtime, boolean useInterface) {
+ if (!useInterface) {
+ return StringUtils.joinLines("Hello!", "");
+ }
+
+ switch (compiler) {
+ case D8:
+ case DX:
+ switch (runtime.asDex().getVm().getVersion()) {
+ case V4_0_4:
+ case V4_4_4:
+ case V10_0_0:
+ return StringUtils.joinLines("Hello!", "Goodbye!", "");
+
+ case V7_0_0:
+ return StringUtils.joinLines(
+ "Hello!",
+ "Unexpected outcome of checkcast",
+ "Unexpected outcome of instanceof",
+ "Goodbye!",
+ "");
+
+ default:
+ // Fallthrough.
+ }
+
+ case R8:
+ case PROGUARD:
+ return StringUtils.joinLines(
+ "Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
+
+ case R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES:
+ return StringUtils.joinLines(
+ "Hello!",
+ "Unexpected outcome of getstatic",
+ "Unexpected outcome of checkcast",
+ "Goodbye!",
+ "");
+
+ case JAVAC:
+ return StringUtils.joinLines("Hello!", "Goodbye!", "");
+
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ @Override
+ public String instruction() {
+ return "invokestatic UnverifiableClass/unverifiableMethod()V";
+ }
+ },
+ INVOKE_VERIFIABLE_METHOD_ON_UNVERIFIABLE_CLASS {
+
+ @Override
+ public String getExpectedOutput(
+ Compiler compiler, TestRuntime runtime, boolean useInterface) {
+ if (useInterface) {
+ return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
+ }
+
+ switch (compiler) {
+ case R8:
+ case R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES:
+ case PROGUARD:
+ // The unverifiable method has been removed as a result of tree shaking, so the code
+ // does not fail with a verification error when trying to load class UnverifiableClass.
+ return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
+
+ default:
+ // The code fails with a verification error because the verifiableMethod() is being
+ // called on UnverifiableClass, which does not verify due to unverifiableMethod().
+ return StringUtils.joinLines("Hello!", "");
+ }
+ }
+
+ @Override
+ public String instruction() {
+ return "invokestatic UnverifiableClass/verifiableMethod()V";
+ }
+ };
+
+ public abstract String getExpectedOutput(
+ Compiler compiler, TestRuntime runtime, boolean useInterface);
+
+ public abstract String instruction();
}
private final TestParameters parameters;
@@ -191,11 +272,17 @@
assert parameters.isDexRuntime();
DXTestRunResult dxResult =
- testForDX().addProgramFiles(inputJar).run(parameters.getRuntime(), mainClass.name);
+ testForDX()
+ .addProgramFiles(inputJar)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(dxResult, Compiler.DX);
D8TestRunResult d8Result =
- testForD8().addProgramFiles(inputJar).run(parameters.getRuntime(), mainClass.name);
+ testForD8()
+ .addProgramFiles(inputJar)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(d8Result, Compiler.D8);
}
@@ -254,14 +341,6 @@
}
private void checkTestRunResult(TestRunResult<?> result, Compiler compiler) {
- Assume.assumeFalse(
- "Triage (b/144966342)",
- parameters.getRuntime().isDex()
- && parameters
- .getRuntime()
- .asDex()
- .getMinApiLevel()
- .isGreaterThanOrEqualTo(AndroidApiLevel.Q));
switch (mode) {
case NO_INVOKE:
result.assertSuccessWithOutput(getExpectedOutput(compiler));
@@ -309,58 +388,7 @@
}
private String getExpectedOutput(Compiler compiler) {
- if (mode == Mode.NO_INVOKE) {
- return StringUtils.joinLines("Hello!", "Goodbye!", "");
- }
- if (mode == Mode.INVOKE_VERIFIABLE_METHOD_ON_UNVERIFIABLE_CLASS) {
- if (useInterface) {
- return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
- } else {
- if (compiler == Compiler.R8
- || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
- || compiler == Compiler.PROGUARD) {
- // The unverifiable method has been removed as a result of tree shaking, so the code does
- // not fail with a verification error when trying to load class `UnverifiableClass`.
- return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
- } else {
- // The code fails with a verification error because the verifiableMethod() is being called
- // on `UnverifiableClass`, which does not verify due to unverifiableMethod().
- return StringUtils.joinLines("Hello!", "");
- }
- }
- }
- assert mode == Mode.INVOKE_UNVERIFIABLE_METHOD;
- if (useInterface) {
- if (compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES) {
- return StringUtils.joinLines(
- "Hello!",
- "Unexpected outcome of getstatic",
- "Unexpected outcome of checkcast",
- "Goodbye!",
- "");
- } else if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
- return StringUtils.joinLines("Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
- } else if (compiler == Compiler.DX || compiler == Compiler.D8) {
- if (ToolHelper.getDexVm().getVersion() == Version.V4_0_4
- || ToolHelper.getDexVm().getVersion() == Version.V4_4_4) {
- return StringUtils.joinLines("Hello!", "Goodbye!", "");
- } else if (ToolHelper.getDexVm().getVersion() == Version.V7_0_0) {
- return StringUtils.joinLines(
- "Hello!",
- "Unexpected outcome of checkcast",
- "Unexpected outcome of instanceof",
- "Goodbye!",
- "");
- } else {
- return StringUtils.joinLines("Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
- }
- } else {
- assert compiler == Compiler.JAVAC;
- return StringUtils.joinLines("Hello!", "Goodbye!", "");
- }
- } else {
- return StringUtils.joinLines("Hello!", "");
- }
+ return mode.getExpectedOutput(compiler, parameters.getRuntime(), useInterface);
}
private Matcher<String> getMatcherForExpectedError() {
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 3b39b815..4cada28 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -766,7 +766,7 @@
new ProguardConfigurationParser(new DexItemFactory(), reporter);
parser.parse(path);
fail("Expect to fail due to the lack of file name.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(handler.errors, path, 6, 14, "File name expected");
}
}
@@ -795,7 +795,7 @@
new ProguardConfigurationParser(new DexItemFactory(), reporter)
.parse(path);
fail();
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(handler.errors, path, 6, 10,"does-not-exist.flags");
}
}
@@ -807,7 +807,7 @@
new ProguardConfigurationParser(new DexItemFactory(), reporter)
.parse(path);
fail();
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(handler.errors, path, 6,2, "does-not-exist.flags");
}
}
@@ -832,7 +832,7 @@
parser.parse(createConfigurationForTesting(
Collections.singletonList("-injars abc.jar(*.zip;*.class)")));
fail();
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
assertEquals(1, handler.errors.size());
}
}
@@ -1026,7 +1026,7 @@
reset();
parser.parse(createConfigurationForTesting(ImmutableList.of(option)));
fail("Expect to fail due to unsupported option.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(handler.errors, null, 1, 1, "Unsupported option", option);
}
}
@@ -1427,7 +1427,7 @@
new ProguardConfigurationParser(new DexItemFactory(), reporter);
parser.parse(createConfigurationForTesting(ImmutableList.of("-keepattributes xxx,")));
fail();
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
assertTrue(
handler.errors.get(0).getDiagnosticMessage().contains("Expected list element at "));
}
@@ -1613,7 +1613,7 @@
reset();
parser.parse(createConfigurationForTesting(ImmutableList.of(option + " ,")));
fail("Expect to fail due to the lack of path filter.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(handler.errors, null, 1, option.length() + 2, "Path filter expected");
}
}
@@ -1628,7 +1628,7 @@
reset();
parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,,yyy")));
fail("Expect to fail due to the lack of path filter.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(handler.errors, null, 1, option.length() + 6, "Path filter expected");
}
}
@@ -1643,7 +1643,7 @@
reset();
parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,")));
fail("Expect to fail due to the lack of path filter.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(handler.errors, null, 1, option.length() + 6, "Path filter expected");
}
}
@@ -1819,7 +1819,7 @@
try {
parser.parse(proguardConfig);
fail("Expect to fail due to unsupported constructor name pattern.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(
handler.errors, proguardConfig, 5, 3, "Unexpected character", "method name");
}
@@ -2501,7 +2501,7 @@
try {
parser.parse(createConfigurationForTesting(ImmutableList.of("-printusage <>")));
fail("Expect to fail due to the lack of file name.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(handler.errors, null, 1, 15, "Value of system property '' not found");
}
}
@@ -2518,7 +2518,7 @@
parser.parse(
createConfigurationForTesting(ImmutableList.of("-printusage <" + property + ">")));
fail("Expect to fail due to the lack of file name.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(
handler.errors, null, 1, 16, "Value of system property '" + property + "' not found");
}
@@ -2539,7 +2539,7 @@
reset();
parser.parse(createConfigurationForTesting(ImmutableList.of(flag + " " + value)));
fail("Expect to fail due to un-closed quote.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checker.get();
}
}
@@ -2759,7 +2759,7 @@
new ProguardConfigurationParser(new DexItemFactory(), reporter)
.parse(proguardConfigurationFile);
fail("Expected to fail since the type name cannot be negated.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
checkDiagnostics(
handler.errors,
proguardConfigurationFile,
@@ -2794,7 +2794,7 @@
try {
parser.parse(proguardConfig);
fail("Expect to fail due to unsupported constructor name pattern.");
- } catch (AbortException e) {
+ } catch (RuntimeException e) {
int column = initName.contains("void") ? initName.indexOf("void") + 8
: (initName.contains("XYZ") ? initName.indexOf(">") + 4 : 3);
if (initName.contains("XYZ")) {
diff --git a/third_party/kotlinx-coroutines-1.3.6.tar.gz.sha1 b/third_party/kotlinx-coroutines-1.3.6.tar.gz.sha1
new file mode 100644
index 0000000..6b4a3b8
--- /dev/null
+++ b/third_party/kotlinx-coroutines-1.3.6.tar.gz.sha1
@@ -0,0 +1 @@
+fe2b5c46bec807d11fcbd5572f9f7ba9d755cefd
\ No newline at end of file
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index ad9f63f..809a1fe 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -91,6 +91,12 @@
help='Archive find-min-xmx results on GCS',
default=False,
action='store_true')
+ result.add_option('--no-extra-pgconf', '--no_extra_pgconf',
+ help='Build without the following extra rules: ' +
+ '-printconfiguration, -printmapping, -printseeds, ' +
+ '-printusage',
+ default=False,
+ action='store_true')
result.add_option('--timeout',
type='int',
default=0,
@@ -529,6 +535,8 @@
args.extend(['--main-dex-rules', rules])
if 'allow-type-errors' in values:
extra_args.append('-Dcom.android.tools.r8.allowTypeErrors=1')
+ extra_args.append(
+ '-Dcom.android.tools.r8.disallowClassInlinerGracefulExit=1')
if options.debug_agent:
if not options.compiler_build == 'full':
@@ -575,9 +583,10 @@
pg_outdir = os.path.dirname(outdir)
else:
pg_outdir = outdir
- additional_pg_conf = GenerateAdditionalProguardConfiguration(
- temp, os.path.abspath(pg_outdir))
- args.extend(['--pg-conf', additional_pg_conf])
+ if not options.no_extra_pgconf:
+ additional_pg_conf = GenerateAdditionalProguardConfiguration(
+ temp, os.path.abspath(pg_outdir))
+ args.extend(['--pg-conf', additional_pg_conf])
build = not options.no_build and not options.golem
stderr_path = os.path.join(temp, 'stderr')
with open(stderr_path, 'w') as stderr: