Merge commit '9efb73abbd817be724dd0ec141618787460a1778' into 1.7.8-dev
diff --git a/.gitignore b/.gitignore
index 4062928..b74632a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,6 +103,8 @@
third_party/rhino-1.7.10.tar.gz
third_party/rhino-android-1.1.1
third_party/rhino-android-1.1.1.tar.gz
+third_party/iosched_2019
+third_party/iosched_2019.tar.gz
third_party/android_cts_baseline
third_party/android_cts_baseline.tar.gz
third_party/framework
diff --git a/build.gradle b/build.gradle
index 15d8deb..89e6798 100644
--- a/build.gradle
+++ b/build.gradle
@@ -304,6 +304,7 @@
"ddmlib",
"gradle/gradle",
"google-java-format",
+ "iosched_2019",
"jacoco",
"jasmin",
"jctf",
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 9eccf41..b0d5055 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
@@ -509,10 +508,6 @@
}
reporter.error(builder.toString());
}
- if (hasDesugaredLibraryConfiguration()) {
- reporter.warning(
- new StringDiagnostic("Desugared library configuration is still work in progress"));
- }
super.validate();
}
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index a7d57b1..c56ea8a 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -821,7 +821,7 @@
if (whyAreYouKeepingConsumer != null) {
for (DexReference reference : rootSet.reasonAsked) {
whyAreYouKeepingConsumer.printWhyAreYouKeeping(
- enqueuer.getGraphNode(reference), System.out);
+ enqueuer.getGraphReporter().getGraphNode(reference), System.out);
}
}
if (rootSet.checkDiscarded.isEmpty()
@@ -851,7 +851,8 @@
if (!failed.isEmpty()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
whyAreYouKeepingConsumer.printWhyAreYouKeeping(
- enqueuer.getGraphNode(definition.toReference()), new PrintStream(baos));
+ enqueuer.getGraphReporter().getGraphNode(definition.toReference()),
+ new PrintStream(baos));
options.reporter.info(
new StringDiagnostic(
"Item " + definition.toSourceString() + " was not discarded.\n" + baos.toString()));
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index ae68adb..f4b3763 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -101,7 +101,8 @@
private final List<FeatureSplit> featureSplits = new ArrayList<>();
private boolean allowPartiallyImplementedProguardOptions = false;
- private boolean allowTestProguardOptions = false;
+ private boolean allowTestProguardOptions =
+ System.getProperty("com.android.tools.r8.allowTestProguardOptions") != null;
// TODO(zerny): Consider refactoring CompatProguardCommandBuilder to avoid subclassing.
Builder() {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index d7f3861..ad763d8 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -37,6 +37,7 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.ProguardMapSupplier;
@@ -218,8 +219,18 @@
if (inputChecksums.containsKey(clazz.getType().descriptor.toASCIIString())) {
continue;
} else {
- throw new CompilationError(clazz + " from " + clazz.origin +
- " has no checksum information while checksum encoding is requested");
+ String name = clazz.toSourceString();
+ if (name.contains(DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX)
+ || name.contains(DesugaredLibraryWrapperSynthesizer.VIVIFIED_TYPE_WRAPPER_SUFFIX)) {
+ synthesizedChecksums.put(
+ clazz.getType().descriptor.toASCIIString(), (long) name.hashCode());
+ } else {
+ throw new CompilationError(
+ clazz
+ + " from "
+ + clazz.origin
+ + " has no checksum information while checksum encoding is requested");
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index dfe6a37..f34e2bc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -363,11 +363,6 @@
AppInfoWithSubtyping appInfo,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
checkIfObsolete();
- if (isClassInitializer()) {
- // This will probably never happen but never inline a class initializer.
- whyAreYouNotInliningReporter.reportUnknownReason();
- return false;
- }
if (inliningReason == Reason.FORCE) {
// Make sure we would be able to inline this normally.
@@ -389,33 +384,36 @@
if (appInfo.isSubtype(containerType, method.holder)) {
return true;
}
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportCallerNotSubtype();
return false;
case PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE:
if (containerType.isSamePackage(method.holder)) {
return true;
}
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportCallerNotSamePackage();
return false;
case PROCESSED_INLINING_CANDIDATE_SAME_NEST:
if (NestUtils.sameNest(containerType, method.holder, appInfo)) {
return true;
}
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportCallerNotSameNest();
return false;
case PROCESSED_INLINING_CANDIDATE_SAME_CLASS:
if (containerType == method.holder) {
return true;
}
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportCallerNotSameClass();
return false;
case PROCESSED_NOT_INLINING_CANDIDATE:
+ whyAreYouNotInliningReporter.reportInlineeNotInliningCandidate();
+ return false;
+
case NOT_PROCESSED:
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportInlineeNotProcessed();
return false;
default:
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 3bdaf06..0cc264a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -156,6 +156,7 @@
public final DexString compareToIgnoreCaseMethodName = createString("compareToIgnoreCase");
public final DexString cloneMethodName = createString("clone");
public final DexString substringName = createString("substring");
+ public final DexString trimName = createString("trim");
public final DexString valueOfMethodName = createString("valueOf");
public final DexString toStringMethodName = createString("toString");
@@ -882,6 +883,8 @@
public final DexMethod toString;
public final DexMethod intern;
+ public final DexMethod trim = createMethod(stringType, createProto(stringType), trimName);
+
private StringMethods() {
isEmpty = createMethod(
stringDescriptor, isEmptyMethodName, booleanDescriptor, DexString.EMPTY_ARRAY);
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 3ba63c8..0fcb5b6 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -456,7 +456,7 @@
clazz.asProgramClass().setInitialClassFileVersion(version);
if (application.options.encodeChecksums) {
CRC32 crc = new CRC32();
- crc.update(this.context.classCache);
+ crc.update(this.context.classCache, 0, this.context.classCache.length);
checksums.addChecksum(type.descriptor.toASCIIString(), crc.getValue());
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index 7ad4c5b..2b55be8 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -9,12 +9,11 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerWorklist;
-import com.android.tools.r8.shaking.KeepReason;
public abstract class EnqueuerAnalysis {
/** Called when a class is found to be instantiated. */
- public void processNewlyInstantiatedClass(DexProgramClass clazz, KeepReason reason) {}
+ public void processNewlyInstantiatedClass(DexProgramClass clazz, DexEncodedMethod context) {}
/** Called when a field is found to be live. */
public void processNewlyLiveField(DexEncodedField field) {}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
index 54b03a6..e7c6418 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -7,9 +7,9 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.KeepReason;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -61,10 +61,10 @@
}
@Override
- public void processNewlyInstantiatedClass(DexProgramClass clazz, KeepReason reason) {
+ public void processNewlyInstantiatedClass(DexProgramClass clazz, DexEncodedMethod context) {
DexType key = clazz.type;
DexType objectType = appView.dexItemFactory().objectType;
- if (!reason.isInstantiatedIn()) {
+ if (context == null) {
// Record that we don't know anything about the set of classes that are guaranteed to be
// initialized in the instance methods of `clazz`.
mapping.put(key, objectType);
@@ -73,7 +73,7 @@
// Record that the enclosing class is guaranteed to be initialized at the allocation site.
AppInfoWithSubtyping appInfo = appView.appInfo();
- DexType guaranteedToBeInitialized = reason.asInstantiatedIn().getMethod().holder;
+ DexType guaranteedToBeInitialized = context.method.holder;
DexType existingGuaranteedToBeInitialized =
mapping.getOrDefault(key, guaranteedToBeInitialized);
mapping.put(
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 e5e86fe..b790829 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
@@ -13,8 +13,9 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
+import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
-import com.android.tools.r8.ir.optimize.InliningOracle;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import java.util.Collection;
@@ -68,8 +69,8 @@
public abstract InlineAction computeInlining(
DexEncodedMethod singleTarget,
- InliningOracle decider,
- DexMethod invocationContext,
+ Reason reason,
+ DefaultInliningOracle decider,
ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
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 7276e77..aea5a70 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
@@ -13,8 +13,9 @@
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
-import com.android.tools.r8.ir.optimize.InliningOracle;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import java.util.List;
@@ -42,12 +43,12 @@
@Override
public final InlineAction computeInlining(
DexEncodedMethod singleTarget,
- InliningOracle decider,
- DexMethod invocationContext,
+ Reason reason,
+ DefaultInliningOracle decider,
ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
return decider.computeForInvokeWithReceiver(
- this, singleTarget, invocationContext, whyAreYouNotInliningReporter);
+ this, singleTarget, reason, whyAreYouNotInliningReporter);
}
@Override
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 3379b61..f7b8688 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
@@ -16,10 +16,11 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.InliningConstraints;
-import com.android.tools.r8.ir.optimize.InliningOracle;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import java.util.Collection;
import java.util.List;
@@ -139,8 +140,8 @@
@Override
public InlineAction computeInlining(
DexEncodedMethod singleTarget,
- InliningOracle decider,
- DexMethod invocationContext,
+ Reason reason,
+ DefaultInliningOracle decider,
ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
// We never determine a single target for 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 078b220..acd864e 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
@@ -18,10 +18,11 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.InliningConstraints;
-import com.android.tools.r8.ir.optimize.InliningOracle;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collection;
@@ -141,16 +142,12 @@
@Override
public InlineAction computeInlining(
DexEncodedMethod singleTarget,
- InliningOracle decider,
- DexMethod invocationContext,
+ Reason reason,
+ DefaultInliningOracle decider,
ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
return decider.computeForInvokeStatic(
- this,
- singleTarget,
- invocationContext,
- classInitializationAnalysis,
- whyAreYouNotInliningReporter);
+ this, singleTarget, reason, classInitializationAnalysis, whyAreYouNotInliningReporter);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index f6f34e6..be6ffb2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -1304,15 +1304,15 @@
DexItemFactory factory = appView.dexItemFactory();
String unqualifiedName = method.holder.getName();
// Avoid duplicate class names between core lib dex file and program dex files.
- String coreLibUtilitySuffix =
- appView.options().isDesugaredLibraryCompilation() ? "$corelib" : "";
+ String desugaredLibUtilitySuffix =
+ appView.options().isDesugaredLibraryCompilation() ? "$desugaredLib" : "";
String descriptor =
UTILITY_CLASS_DESCRIPTOR_PREFIX
+ '$'
+ unqualifiedName
+ '$'
+ method.proto.parameters.size()
- + coreLibUtilitySuffix
+ + desugaredLibUtilitySuffix
+ '$'
+ methodName
+ ';';
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
index 95a70df..1c2e894 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
@@ -144,7 +144,7 @@
throws ExecutionException {
processNestsConcurrently(executorService);
addDeferredBridges();
- synthetizeNestConstructor(builder);
+ synthesizeNestConstructor(builder);
optimizeDeferredBridgesConcurrently(executorService, converter);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index b9a758a..f0eda16 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -31,9 +32,12 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -59,7 +63,7 @@
private final AppView<?> appView;
private final DexItemFactory factory;
private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
- private final Map<DexClass, List<DexEncodedMethod>> callBackMethods = new HashMap<>();
+ private final Map<DexClass, Set<DexEncodedMethod>> callBackMethods = new HashMap<>();
public DesugaredLibraryAPIConverter(AppView<?> appView) {
this.appView = appView;
@@ -75,27 +79,31 @@
generateCallBackIfNeeded(code);
- InstructionListIterator iterator = code.instructionListIterator();
- while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (!instruction.isInvokeMethod()) {
- continue;
- }
- InvokeMethod invokeMethod = instruction.asInvokeMethod();
- DexMethod invokedMethod = invokeMethod.getInvokedMethod();
- // Rewritting is required only on calls to library methods which are not desugared.
- if (appView.rewritePrefix.hasRewrittenType(invokedMethod.holder)
- || invokedMethod.holder.isArrayType()) {
- continue;
- }
- DexClass dexClass = appView.definitionFor(invokedMethod.holder);
- if (dexClass == null || !dexClass.isLibraryClass()) {
- continue;
- }
- // Library methods do not understand desugared types, hence desugared types have to be
- // converted around non desugared library calls for the invoke to resolve.
- if (appView.rewritePrefix.hasRewrittenTypeInSignature(invokedMethod.proto)) {
- rewriteLibraryInvoke(code, invokeMethod, iterator);
+ ListIterator<BasicBlock> blockIterator = code.listIterator();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ InstructionListIterator iterator = block.listIterator(code);
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (!instruction.isInvokeMethod()) {
+ continue;
+ }
+ InvokeMethod invokeMethod = instruction.asInvokeMethod();
+ DexMethod invokedMethod = invokeMethod.getInvokedMethod();
+ // Rewriting is required only on calls to library methods which are not desugared.
+ if (appView.rewritePrefix.hasRewrittenType(invokedMethod.holder)
+ || invokedMethod.holder.isArrayType()) {
+ continue;
+ }
+ DexClass dexClass = appView.definitionFor(invokedMethod.holder);
+ if (dexClass == null || !dexClass.isLibraryClass()) {
+ continue;
+ }
+ // Library methods do not understand desugared types, hence desugared types have to be
+ // converted around non desugared library calls for the invoke to resolve.
+ if (appView.rewritePrefix.hasRewrittenTypeInSignature(invokedMethod.proto)) {
+ rewriteLibraryInvoke(code, invokeMethod, iterator, blockIterator);
+ }
}
}
}
@@ -158,7 +166,7 @@
private synchronized void generateCallBack(DexClass dexClass, DexEncodedMethod originalMethod) {
DexMethod methodToInstall =
- methodWithVivifiedTypeInSignature(originalMethod.method, dexClass.type);
+ methodWithVivifiedTypeInSignature(originalMethod.method, dexClass.type, appView);
CfCode cfCode =
new APIConverterWrapperCfCodeProvider(
appView, originalMethod.method, null, this, dexClass.isInterface())
@@ -170,27 +178,27 @@
}
private synchronized void addCallBackSignature(DexClass dexClass, DexEncodedMethod method) {
- callBackMethods.putIfAbsent(dexClass, new ArrayList<>());
- List<DexEncodedMethod> dexEncodedMethods = callBackMethods.get(dexClass);
- dexEncodedMethods.add(method);
+ callBackMethods.putIfAbsent(dexClass, new HashSet<>());
+ callBackMethods.get(dexClass).add(method);
}
- DexMethod methodWithVivifiedTypeInSignature(DexMethod originalMethod, DexType holder) {
+ public static DexMethod methodWithVivifiedTypeInSignature(
+ DexMethod originalMethod, DexType holder, AppView<?> appView) {
DexType[] newParameters = originalMethod.proto.parameters.values.clone();
int index = 0;
for (DexType param : originalMethod.proto.parameters.values) {
if (appView.rewritePrefix.hasRewrittenType(param)) {
- newParameters[index] = this.vivifiedTypeFor(param);
+ newParameters[index] = vivifiedTypeFor(param, appView);
}
index++;
}
DexType returnType = originalMethod.proto.returnType;
DexType newReturnType =
appView.rewritePrefix.hasRewrittenType(returnType)
- ? this.vivifiedTypeFor(returnType)
+ ? vivifiedTypeFor(returnType, appView)
: returnType;
- DexProto newProto = factory.createProto(newReturnType, newParameters);
- return factory.createMethod(holder, newProto, originalMethod.name);
+ DexProto newProto = appView.dexItemFactory().createProto(newReturnType, newParameters);
+ return appView.dexItemFactory().createMethod(holder, newProto, originalMethod.name);
}
public void generateWrappers(
@@ -198,8 +206,7 @@
throws ExecutionException {
wrapperSynthesizor.finalizeWrappers(builder, irConverter, executorService);
for (DexClass dexClass : callBackMethods.keySet()) {
- // TODO(b/134732760): add the methods in the root set.
- List<DexEncodedMethod> dexEncodedMethods = callBackMethods.get(dexClass);
+ Set<DexEncodedMethod> dexEncodedMethods = callBackMethods.get(dexClass);
dexClass.appendVirtualMethods(dexEncodedMethods);
irConverter.optimizeSynthesizedMethodsConcurrently(dexEncodedMethods, executorService);
}
@@ -223,15 +230,20 @@
+ " is a desugared type)."));
}
- public DexType vivifiedTypeFor(DexType type) {
+ public static DexType vivifiedTypeFor(DexType type, AppView<?> appView) {
DexType vivifiedType =
- factory.createType(DescriptorUtils.javaTypeToDescriptor(VIVIFIED_PREFIX + type.toString()));
+ appView
+ .dexItemFactory()
+ .createType(DescriptorUtils.javaTypeToDescriptor(VIVIFIED_PREFIX + type.toString()));
appView.rewritePrefix.rewriteType(vivifiedType, type);
return vivifiedType;
}
private void rewriteLibraryInvoke(
- IRCode code, InvokeMethod invokeMethod, InstructionListIterator iterator) {
+ IRCode code,
+ InvokeMethod invokeMethod,
+ InstructionListIterator iterator,
+ ListIterator<BasicBlock> blockIterator) {
DexMethod invokedMethod = invokeMethod.getInvokedMethod();
// Create return conversion if required.
@@ -240,7 +252,7 @@
DexType returnType = invokedMethod.proto.returnType;
if (appView.rewritePrefix.hasRewrittenType(returnType)) {
if (canConvert(returnType)) {
- newReturnType = vivifiedTypeFor(returnType);
+ newReturnType = vivifiedTypeFor(returnType, appView);
// Return conversion added only if return value is used.
if (invokeMethod.outValue() != null
&& invokeMethod.outValue().numberOfUsers() + invokeMethod.outValue().numberOfPhiUsers()
@@ -270,7 +282,7 @@
DexType argType = parameters[i];
if (appView.rewritePrefix.hasRewrittenType(argType)) {
if (canConvert(argType)) {
- DexType argVivifiedType = vivifiedTypeFor(argType);
+ DexType argVivifiedType = vivifiedTypeFor(argType, appView);
Value inValue = invokeMethod.inValues().get(i + receiverShift);
newParameters[i] = argVivifiedType;
parameterConversions.add(
@@ -296,6 +308,8 @@
newDexMethod.proto,
invokeMethod.outValue(),
newInValues);
+ assert newDexMethod
+ == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView);
// Insert and reschedule all instructions.
iterator.previous();
@@ -310,6 +324,41 @@
returnConversion.setPosition(invokeMethod.getPosition());
iterator.add(returnConversion);
}
+
+ // If the invoke is in a try-catch, since all conversions can throw, the basic block needs
+ // to be split in between each invoke...
+ if (newInvokeMethod.getBlock().hasCatchHandlers()) {
+ splitIfCatchHandlers(code, newInvokeMethod.getBlock(), blockIterator);
+ }
+ }
+
+ private void splitIfCatchHandlers(
+ IRCode code,
+ BasicBlock blockWithIncorrectThrowingInstructions,
+ ListIterator<BasicBlock> blockIterator) {
+ InstructionListIterator instructionsIterator =
+ blockWithIncorrectThrowingInstructions.listIterator(code);
+ BasicBlock currentBlock = blockWithIncorrectThrowingInstructions;
+ while (currentBlock != null && instructionsIterator.hasNext()) {
+ Instruction throwingInstruction =
+ instructionsIterator.nextUntil(Instruction::instructionTypeCanThrow);
+ BasicBlock nextBlock;
+ if (throwingInstruction != null) {
+ nextBlock = instructionsIterator.split(code, blockIterator);
+ // Back up to before the split before inserting catch handlers.
+ blockIterator.previous();
+ nextBlock.copyCatchHandlers(code, blockIterator, currentBlock, appView.options());
+ BasicBlock b = blockIterator.next();
+ assert b == nextBlock;
+ // Switch iteration to the split block.
+ instructionsIterator = nextBlock.listIterator(code);
+ currentBlock = nextBlock;
+ } else {
+ assert !instructionsIterator.hasNext();
+ instructionsIterator = null;
+ currentBlock = null;
+ }
+ }
}
private Instruction createParameterConversion(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index a9c70ca..95db2cf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -94,6 +94,7 @@
// }
public class DesugaredLibraryWrapperSynthesizer {
+ public static final String WRAPPER_PREFIX = "$r8$wrapper$";
public static final String TYPE_WRAPPER_SUFFIX = "$-WRP";
public static final String VIVIFIED_TYPE_WRAPPER_SUFFIX = "$-V-WRP";
@@ -111,14 +112,13 @@
private final DexItemFactory factory;
private final DesugaredLibraryAPIConverter converter;
- public DesugaredLibraryWrapperSynthesizer(
- AppView<?> appView, DesugaredLibraryAPIConverter converter) {
+ DesugaredLibraryWrapperSynthesizer(AppView<?> appView, DesugaredLibraryAPIConverter converter) {
this.appView = appView;
this.factory = appView.dexItemFactory();
this.converter = converter;
}
- public boolean hasSynthesized(DexType type) {
+ boolean hasSynthesized(DexType type) {
return generatedWrappers.contains(type);
}
@@ -126,7 +126,7 @@
// 1. Generate wrappers without conversion methods.
// 2. Compute wrapper types.
- public boolean canGenerateWrapper(DexType type) {
+ boolean canGenerateWrapper(DexType type) {
DexClass dexClass = appView.definitionFor(type);
if (dexClass == null) {
return false;
@@ -134,11 +134,11 @@
return dexClass.isLibraryClass();
}
- public DexType getTypeWrapper(DexType type) {
+ DexType getTypeWrapper(DexType type) {
return getWrapper(type, TYPE_WRAPPER_SUFFIX, typeWrappers, this::generateTypeWrapper);
}
- public DexType getVivifiedTypeWrapper(DexType type) {
+ DexType getVivifiedTypeWrapper(DexType type) {
return getWrapper(
type,
VIVIFIED_TYPE_WRAPPER_SUFFIX,
@@ -146,6 +146,12 @@
this::generateVivifiedTypeWrapper);
}
+ private DexType createWrapperType(DexType type, String suffix) {
+ return factory.createType(
+ DescriptorUtils.javaTypeToDescriptor(
+ WRAPPER_PREFIX + type.toString().replace('.', '$') + suffix));
+ }
+
private DexType getWrapper(
DexType type,
String suffix,
@@ -163,9 +169,7 @@
type,
t -> {
toGenerate.set(true);
- DexType wrapperType =
- factory.createType(
- DescriptorUtils.javaTypeToDescriptor(type.toString() + suffix));
+ DexType wrapperType = createWrapperType(type, suffix);
generatedWrappers.add(wrapperType);
return new Pair<>(wrapperType, null);
});
@@ -189,21 +193,25 @@
return pair.getFirst();
}
- public DexProgramClass generateTypeWrapper(DexClass dexClass, DexType typeWrapperType) {
+ private DexType vivifiedTypeFor(DexType type) {
+ return DesugaredLibraryAPIConverter.vivifiedTypeFor(type, appView);
+ }
+
+ private DexProgramClass generateTypeWrapper(DexClass dexClass, DexType typeWrapperType) {
DexType type = dexClass.type;
DexEncodedField wrapperField = synthesizeWrappedValueField(typeWrapperType, type);
return synthesizeWrapper(
- converter.vivifiedTypeFor(type),
+ vivifiedTypeFor(type),
dexClass,
synthesizeVirtualMethodsForTypeWrapper(dexClass.asLibraryClass(), wrapperField),
wrapperField);
}
- public DexProgramClass generateVivifiedTypeWrapper(
+ private DexProgramClass generateVivifiedTypeWrapper(
DexClass dexClass, DexType vivifiedTypeWrapperType) {
DexType type = dexClass.type;
DexEncodedField wrapperField =
- synthesizeWrappedValueField(vivifiedTypeWrapperType, converter.vivifiedTypeFor(type));
+ synthesizeWrappedValueField(vivifiedTypeWrapperType, vivifiedTypeFor(type));
return synthesizeWrapper(
type,
dexClass,
@@ -307,8 +315,8 @@
DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder);
assert holderClass != null;
DexMethod methodToInstall =
- converter.methodWithVivifiedTypeInSignature(
- dexEncodedMethod.method, wrapperField.field.holder);
+ DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
+ dexEncodedMethod.method, wrapperField.field.holder, appView);
CfCode cfCode;
if (dexEncodedMethod.isFinal()) {
invalidWrappers.add(wrapperField.field.holder);
@@ -347,7 +355,7 @@
appView
.options()
.reporter
- .warning(
+ .info(
new StringDiagnostic(
"Desugared library API conversion: cannot wrap final methods "
+ Arrays.toString(methodArray)
@@ -382,8 +390,7 @@
// This looks quadratic but given the size of the collections met in practice for
// desugared libraries (Max ~15) it does not matter.
for (DexEncodedMethod alreadyImplementedMethod : implementedMethods) {
- if (alreadyImplementedMethod.method.proto == virtualMethod.method.proto
- && alreadyImplementedMethod.method.name == virtualMethod.method.name) {
+ if (alreadyImplementedMethod.method.match(virtualMethod.method)) {
alreadyAdded = true;
continue;
}
@@ -409,7 +416,7 @@
appView
.options()
.reporter
- .warning(
+ .info(
new StringDiagnostic(
"Desugared library API conversion: Generating a large wrapper for "
+ libraryClass.type
@@ -455,7 +462,7 @@
// 2. Add the synthesized classes.
// 3. Process all methods.
- public void finalizeWrappers(
+ void finalizeWrappers(
DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
throws ExecutionException {
finalizeWrappers(
@@ -509,7 +516,7 @@
synthesizedClass.type,
type,
type,
- converter.vivifiedTypeFor(type),
+ vivifiedTypeFor(type),
reverse == null ? null : reverse.getSecond()));
}
@@ -519,7 +526,7 @@
synthesizeConversionMethod(
synthesizedClass.type,
type,
- converter.vivifiedTypeFor(type),
+ vivifiedTypeFor(type),
type,
reverse == null ? null : reverse.getSecond()));
}
@@ -541,7 +548,7 @@
factory.createString(
"Unsupported conversion for "
+ type
- + ". See compilation time warnings for more infos."),
+ + ". See compilation time infos for more details."),
holder)
.generateCfCode();
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index db8f582..b79a316 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -560,7 +560,7 @@
warnMissingEmulatedInterface(interfaceType);
} else if (theInterface.isProgramClass()) {
DexProgramClass synthesizedClass =
- synthetizeEmulateInterfaceLibraryClass(
+ synthesizeEmulateInterfaceLibraryClass(
theInterface.asProgramClass(), emulatedInterfacesHierarchy);
if (synthesizedClass != null) {
builder.addSynthesizedClass(synthesizedClass, isInMainDexList(interfaceType));
@@ -631,7 +631,7 @@
factory.createString(method.name.toString()));
}
- private DexProgramClass synthetizeEmulateInterfaceLibraryClass(
+ private DexProgramClass synthesizeEmulateInterfaceLibraryClass(
DexProgramClass theInterface, Map<DexType, List<DexType>> emulatedInterfacesHierarchy) {
List<DexEncodedMethod> emulationMethods = new ArrayList<>();
for (DexEncodedMethod method : theInterface.methods()) {
@@ -731,7 +731,7 @@
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
- // All synthetized methods are static in this case.
+ // All synthesized methods are static in this case.
emulationMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
DexEncodedMethod.EMPTY_ARRAY,
factory.getSkipNameValidationForTesting(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 449f404..dc9c9a0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -167,7 +167,7 @@
appView.dexItemFactory().getSkipNameValidationForTesting());
}
- void synthetizeNestConstructor(DexApplication.Builder<?> builder) {
+ void synthesizeNestConstructor(DexApplication.Builder<?> builder) {
if (nestConstructorUsed) {
appView.appInfo().addSynthesizedClass(nestConstructor);
builder.addSynthesizedClass(nestConstructor, true);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
index 9ec39ed..a4642cd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
@@ -49,7 +49,7 @@
if (nothingToMap()) {
return appView.graphLense();
}
- synthetizeNestConstructor(appBuilder);
+ synthesizeNestConstructor(appBuilder);
return new NestedPrivateMethodLense(
appView,
getNestConstructorType(),
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 2784e8d..87350e7 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
@@ -83,24 +83,39 @@
DexEncodedMethod singleTarget,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
if (singleTarget == null) {
- throw new Unreachable();
+ throw new Unreachable(
+ "Unexpected attempt to inline invoke that does not have a single target");
+ }
+
+ if (singleTarget.isClassInitializer()) {
+ throw new Unreachable(
+ "Unexpected attempt to invoke a class initializer (`"
+ + singleTarget.method.toSourceString()
+ + "`)");
}
if (!singleTarget.hasCode()) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportInlineeDoesNotHaveCode();
return true;
}
- if (appView.definitionFor(singleTarget.method.holder).isNotProgramClass()) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ DexClass clazz = appView.definitionFor(singleTarget.method.holder);
+ if (!clazz.isProgramClass()) {
+ if (clazz.isClasspathClass()) {
+ whyAreYouNotInliningReporter.reportClasspathMethod();
+ } else {
+ assert clazz.isLibraryClass();
+ whyAreYouNotInliningReporter.reportLibraryMethod();
+ }
return true;
}
// Ignore the implicit receiver argument.
int numberOfArguments =
invoke.arguments().size() - BooleanUtils.intValue(invoke.isInvokeMethodWithReceiver());
- if (numberOfArguments != singleTarget.method.getArity()) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ int arity = singleTarget.method.getArity();
+ if (numberOfArguments != arity) {
+ whyAreYouNotInliningReporter.reportIncorrectArity(numberOfArguments, arity);
return true;
}
@@ -182,7 +197,7 @@
return true;
}
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportMustTriggerClassInitialization();
return false;
}
@@ -194,11 +209,11 @@
private boolean passesInliningConstraints(
InvokeMethod invoke,
- DexEncodedMethod candidate,
+ DexEncodedMethod singleTarget,
Reason reason,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- if (candidate.getOptimizationInfo().neverInline()) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ if (singleTarget.getOptimizationInfo().neverInline()) {
+ whyAreYouNotInliningReporter.reportMarkedAsNeverInline();
return false;
}
@@ -207,15 +222,15 @@
if (method.isInstanceInitializer()
&& appView.options().isGeneratingClassFiles()
&& reason != Reason.FORCE) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportNoInliningIntoConstructorsWhenGeneratingClassFiles();
return false;
}
- if (method == candidate) {
+ if (method == singleTarget) {
// Cannot handle recursive inlining at this point.
// Force inlined method should never be recursive.
- assert !candidate.getOptimizationInfo().forceInline();
- whyAreYouNotInliningReporter.reportUnknownReason();
+ assert !singleTarget.getOptimizationInfo().forceInline();
+ whyAreYouNotInliningReporter.reportRecursiveMethod();
return false;
}
@@ -224,67 +239,64 @@
// or optimized code. Right now this happens for the class class staticizer, as it just
// processes all relevant methods in parallel with the full optimization pipeline enabled.
// TODO(sgjesse): Add this assert "assert !isProcessedConcurrently.test(candidate);"
- if (reason != Reason.FORCE && isProcessedConcurrently.test(candidate)) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ if (reason != Reason.FORCE && isProcessedConcurrently.test(singleTarget)) {
+ whyAreYouNotInliningReporter.reportProcessedConcurrently();
return false;
}
InternalOptions options = appView.options();
if (options.featureSplitConfiguration != null
&& !options.featureSplitConfiguration.inSameFeatureOrBase(
- candidate.method, method.method)) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ singleTarget.method, method.method)) {
+ whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit();
return false;
}
- if (options.testing.validInliningReasons != null
- && !options.testing.validInliningReasons.contains(reason)) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ Set<Reason> validInliningReasons = options.testing.validInliningReasons;
+ if (validInliningReasons != null && !validInliningReasons.contains(reason)) {
+ whyAreYouNotInliningReporter.reportInvalidInliningReason(reason, validInliningReasons);
return false;
}
// Abort inlining attempt if method -> target access is not right.
- if (!inliner.hasInliningAccess(method, candidate)) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ if (!inliner.hasInliningAccess(method, singleTarget)) {
+ whyAreYouNotInliningReporter.reportInaccessible();
return false;
}
- DexClass holder = appView.definitionFor(candidate.method.holder);
-
+ DexClass holder = appView.definitionFor(singleTarget.method.holder);
if (holder.isInterface()) {
// Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception at
// runtime.
- whyAreYouNotInliningReporter.reportUnknownReason();
- return false;
- }
-
- if (holder.isNotProgramClass()) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportIncompatibleClassChangeError();
return false;
}
// Don't inline if target is synchronized.
- if (candidate.accessFlags.isSynchronized()) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ if (singleTarget.accessFlags.isSynchronized()) {
+ whyAreYouNotInliningReporter.reportSynchronizedMethod();
return false;
}
if (reason == Reason.DUAL_CALLER) {
- if (satisfiesRequirementsForSimpleInlining(invoke, candidate)) {
+ if (satisfiesRequirementsForSimpleInlining(invoke, singleTarget)) {
// When we have a method with two call sites, we simply inline the method as we normally do
// when the method is small. We still need to ensure that the other call site is also
// inlined, though. Therefore, we record here that we have seen one of the two call sites
// as we normally do.
- inliner.recordDoubleInliningCandidate(method, candidate);
- } else {
- if (inliner.satisfiesRequirementsForDoubleInlining(method, candidate)) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ inliner.recordDoubleInliningCandidate(method, singleTarget);
+ } else if (inliner.isDoubleInliningEnabled()) {
+ if (!inliner.satisfiesRequirementsForDoubleInlining(method, singleTarget)) {
+ whyAreYouNotInliningReporter.reportInvalidDoubleInliningCandidate();
return false;
}
+ } else {
+ // TODO(b/142300882): Should in principle disallow inlining in this case.
+ inliner.recordDoubleInliningCandidate(method, singleTarget);
}
} else if (reason == Reason.SIMPLE
- && !satisfiesRequirementsForSimpleInlining(invoke, candidate)) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ && !satisfiesRequirementsForSimpleInlining(invoke, singleTarget)) {
+ whyAreYouNotInliningReporter.reportInlineeNotSimple();
return false;
}
@@ -293,8 +305,8 @@
// If we do this it can increase the size of the main dex dependent classes.
if (inliner.mainDexClasses.getRoots().contains(method.method.holder)
&& MainDexDirectReferenceTracer.hasReferencesOutsideFromCode(
- appView.appInfo(), candidate, inliner.mainDexClasses.getRoots())) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ appView.appInfo(), singleTarget, inliner.mainDexClasses.getRoots())) {
+ whyAreYouNotInliningReporter.reportInlineeRefersToClassesNotInMainDex();
return false;
}
// Allow inlining into the classes in the main dex dependent set without restrictions.
@@ -341,16 +353,16 @@
}
@Override
- public InlineAction computeForInvokeWithReceiver(
- InvokeMethodWithReceiver invoke,
+ public InlineAction computeInlining(
+ InvokeMethod invoke,
DexEncodedMethod singleTarget,
- DexMethod invocationContext,
+ ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
if (isSingleTargetInvalid(invoke, singleTarget, whyAreYouNotInliningReporter)) {
return null;
}
- if (inliner.isBlackListed(singleTarget, whyAreYouNotInliningReporter)) {
+ if (inliner.isBlacklisted(singleTarget, whyAreYouNotInliningReporter)) {
return null;
}
@@ -364,10 +376,19 @@
return null;
}
+ return invoke.computeInlining(
+ singleTarget, reason, this, classInitializationAnalysis, whyAreYouNotInliningReporter);
+ }
+
+ public InlineAction computeForInvokeWithReceiver(
+ InvokeMethodWithReceiver invoke,
+ DexEncodedMethod singleTarget,
+ Reason reason,
+ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
Value receiver = invoke.getReceiver();
if (receiver.getTypeLattice().isDefinitelyNull()) {
// A definitely null receiver will throw an error on call site.
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportReceiverDefinitelyNull();
return null;
}
@@ -381,7 +402,7 @@
if (!singleTarget.getOptimizationInfo().checksNullReceiverBeforeAnySideEffect()) {
InternalOptions options = appView.options();
if (!options.enableInliningOfInvokesWithNullableReceivers) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportReceiverMaybeNull();
return null;
}
action.setShouldSynthesizeNullCheckForReceiver();
@@ -390,38 +411,17 @@
return action;
}
- @Override
public InlineAction computeForInvokeStatic(
InvokeStatic invoke,
DexEncodedMethod singleTarget,
- DexMethod invocationContext,
+ Reason reason,
ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- if (isSingleTargetInvalid(invoke, singleTarget, whyAreYouNotInliningReporter)) {
- return null;
- }
-
- if (inliner.isBlackListed(singleTarget, whyAreYouNotInliningReporter)) {
- return null;
- }
-
- Reason reason = computeInliningReason(singleTarget);
- // Determine if this should be inlined no matter how big it is.
- if (!singleTarget.isInliningCandidate(
- method, reason, appView.appInfo(), whyAreYouNotInliningReporter)) {
- return null;
- }
-
// Abort inlining attempt if we can not guarantee class for static target has been initialized.
if (!canInlineStaticInvoke(
invoke, method, singleTarget, classInitializationAnalysis, whyAreYouNotInliningReporter)) {
return null;
}
-
- if (!passesInliningConstraints(invoke, singleTarget, reason, whyAreYouNotInliningReporter)) {
- return null;
- }
-
return new InlineAction(singleTarget, invoke, reason);
}
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 e29070d..cbaa36f 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
@@ -6,14 +6,11 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
-import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -53,19 +50,9 @@
}
@Override
- public InlineAction computeForInvokeWithReceiver(
- InvokeMethodWithReceiver invoke,
+ public InlineAction computeInlining(
+ InvokeMethod invoke,
DexEncodedMethod singleTarget,
- DexMethod invocationContext,
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- return computeForInvoke(invoke);
- }
-
- @Override
- public InlineAction computeForInvokeStatic(
- InvokeStatic invoke,
- DexEncodedMethod singleTarget,
- DexMethod invocationContext,
ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
return computeForInvoke(invoke);
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 f3bdff2..44c72c5 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
@@ -58,7 +58,7 @@
public class Inliner {
protected final AppView<AppInfoWithLiveness> appView;
- private final Set<DexMethod> blackList;
+ private final Set<DexMethod> blacklist;
private final LensCodeRewriter lensCodeRewriter;
final MainDexClasses mainDexClasses;
@@ -74,12 +74,12 @@
LensCodeRewriter lensCodeRewriter) {
Kotlin.Intrinsics intrinsics = appView.dexItemFactory().kotlin.intrinsics;
this.appView = appView;
- this.blackList = ImmutableSet.of(intrinsics.throwNpe, intrinsics.throwParameterIsNullException);
+ this.blacklist = ImmutableSet.of(intrinsics.throwNpe, intrinsics.throwParameterIsNullException);
this.lensCodeRewriter = lensCodeRewriter;
this.mainDexClasses = mainDexClasses;
}
- boolean isBlackListed(
+ boolean isBlacklisted(
DexEncodedMethod encodedMethod, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
DexMethod method = encodedMethod.method;
if (encodedMethod.getOptimizationInfo().forceInline()
@@ -88,28 +88,28 @@
}
if (appView.appInfo().isPinned(method)) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportPinned();
return true;
}
- if (blackList.contains(appView.graphLense().getOriginalMethodSignature(method))) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ if (blacklist.contains(appView.graphLense().getOriginalMethodSignature(method))
+ || TwrCloseResourceRewriter.isSynthesizedCloseResourceMethod(method, appView)) {
+ whyAreYouNotInliningReporter.reportBlacklisted();
return true;
}
if (appView.appInfo().neverInline.contains(method)) {
- whyAreYouNotInliningReporter.reportUnknownReason();
- return true;
- }
-
- if (TwrCloseResourceRewriter.isSynthesizedCloseResourceMethod(method, appView)) {
- whyAreYouNotInliningReporter.reportUnknownReason();
+ whyAreYouNotInliningReporter.reportMarkedAsNeverInline();
return true;
}
return false;
}
+ boolean isDoubleInliningEnabled() {
+ return applyDoubleInlining;
+ }
+
private ConstraintWithTarget instructionAllowedForInlining(
Instruction instruction, InliningConstraints inliningConstraints, DexType invocationContext) {
ConstraintWithTarget result =
@@ -759,12 +759,8 @@
? NopWhyAreYouNotInliningReporter.getInstance()
: WhyAreYouNotInliningReporter.createFor(singleTarget, appView, context);
InlineAction action =
- invoke.computeInlining(
- singleTarget,
- oracle,
- context.method,
- classInitializationAnalysis,
- whyAreYouNotInliningReporter);
+ oracle.computeInlining(
+ invoke, singleTarget, classInitializationAnalysis, whyAreYouNotInliningReporter);
if (action == null) {
assert whyAreYouNotInliningReporter.verifyReasonHasBeenReported();
continue;
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 f75f3ff..676187e 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,12 +5,9 @@
package com.android.tools.r8.ir.optimize;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
-import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
@@ -24,16 +21,9 @@
// TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget(appView, context)!
DexEncodedMethod lookupSingleTarget(InvokeMethod invoke, DexType context);
- InlineAction computeForInvokeWithReceiver(
- InvokeMethodWithReceiver invoke,
+ InlineAction computeInlining(
+ InvokeMethod invoke,
DexEncodedMethod singleTarget,
- DexMethod invocationContext,
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
-
- InlineAction computeForInvokeStatic(
- InvokeStatic invoke,
- DexEncodedMethod singleTarget,
- DexMethod invocationContext,
ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
}
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 8930a2a..f836195 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
@@ -905,11 +905,11 @@
return false;
}
+ InliningOracle oracle = defaultOracle.get();
InlineAction inlineAction =
- invoke.computeInlining(
+ oracle.computeInlining(
+ invoke,
singleTarget,
- defaultOracle.get(),
- method.method,
ClassInitializationAnalysis.trivial(),
NopWhyAreYouNotInliningReporter.getInstance());
if (inlineAction == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
index 0858b45..818b8c2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
@@ -7,6 +7,8 @@
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import java.util.Set;
public class NopWhyAreYouNotInliningReporter extends WhyAreYouNotInliningReporter {
@@ -20,14 +22,92 @@
}
@Override
+ public void reportBlacklisted() {}
+
+ @Override
+ public void reportCallerNotSameClass() {}
+
+ @Override
+ public void reportCallerNotSameNest() {}
+
+ @Override
+ public void reportCallerNotSamePackage() {}
+
+ @Override
+ public void reportCallerNotSubtype() {}
+
+ @Override
+ public void reportClasspathMethod() {}
+
+ @Override
+ public void reportInaccessible() {}
+
+ @Override
+ public void reportIncompatibleClassChangeError() {}
+
+ @Override
+ public void reportIncorrectArity(int numberOfArguments, int arity) {}
+
+ @Override
+ public void reportInlineeDoesNotHaveCode() {}
+
+ @Override
+ public void reportInlineeNotInliningCandidate() {}
+
+ @Override
+ public void reportInlineeNotProcessed() {}
+
+ @Override
+ public void reportInlineeNotSimple() {}
+
+ @Override
+ public void reportInlineeRefersToClassesNotInMainDex() {}
+
+ @Override
+ public void reportInliningAcrossFeatureSplit() {}
+
+ @Override
public void reportInstructionBudgetIsExceeded() {}
@Override
+ public void reportInvalidDoubleInliningCandidate() {}
+
+ @Override
+ public void reportInvalidInliningReason(Reason reason, Set<Reason> validInliningReasons) {}
+
+ @Override
+ public void reportLibraryMethod() {}
+
+ @Override
+ public void reportMarkedAsNeverInline() {}
+
+ @Override
+ public void reportMustTriggerClassInitialization() {}
+
+ @Override
+ public void reportNoInliningIntoConstructorsWhenGeneratingClassFiles() {}
+
+ @Override
+ public void reportPinned() {}
+
+ @Override
public void reportPotentialExplosionInExceptionalControlFlowResolutionBlocks(
int estimatedNumberOfControlFlowResolutionBlocks, int threshold) {}
@Override
- public void reportUnknownReason() {}
+ public void reportProcessedConcurrently() {}
+
+ @Override
+ public void reportReceiverDefinitelyNull() {}
+
+ @Override
+ public void reportReceiverMaybeNull() {}
+
+ @Override
+ public void reportRecursiveMethod() {}
+
+ @Override
+ public void reportSynchronizedMethod() {}
@Override
public void reportUnknownTarget() {}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
index 8834ddf..c560944 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -10,8 +10,10 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collection;
+import java.util.Set;
public abstract class WhyAreYouNotInliningReporter {
@@ -44,12 +46,64 @@
}
}
+ public abstract void reportBlacklisted();
+
+ public abstract void reportCallerNotSameClass();
+
+ public abstract void reportCallerNotSameNest();
+
+ public abstract void reportCallerNotSamePackage();
+
+ public abstract void reportCallerNotSubtype();
+
+ public abstract void reportClasspathMethod();
+
+ public abstract void reportInaccessible();
+
+ public abstract void reportIncompatibleClassChangeError();
+
+ public abstract void reportIncorrectArity(int numberOfArguments, int arity);
+
+ public abstract void reportInlineeDoesNotHaveCode();
+
+ public abstract void reportInlineeNotInliningCandidate();
+
+ public abstract void reportInlineeNotProcessed();
+
+ public abstract void reportInlineeNotSimple();
+
+ public abstract void reportInlineeRefersToClassesNotInMainDex();
+
+ public abstract void reportInliningAcrossFeatureSplit();
+
public abstract void reportInstructionBudgetIsExceeded();
+ public abstract void reportInvalidDoubleInliningCandidate();
+
+ public abstract void reportInvalidInliningReason(Reason reason, Set<Reason> validInliningReasons);
+
+ public abstract void reportLibraryMethod();
+
+ public abstract void reportMarkedAsNeverInline();
+
+ public abstract void reportMustTriggerClassInitialization();
+
+ public abstract void reportNoInliningIntoConstructorsWhenGeneratingClassFiles();
+
+ public abstract void reportPinned();
+
public abstract void reportPotentialExplosionInExceptionalControlFlowResolutionBlocks(
int estimatedNumberOfControlFlowResolutionBlocks, int threshold);
- public abstract void reportUnknownReason();
+ public abstract void reportProcessedConcurrently();
+
+ public abstract void reportReceiverDefinitelyNull();
+
+ public abstract void reportReceiverMaybeNull();
+
+ public abstract void reportRecursiveMethod();
+
+ public abstract void reportSynchronizedMethod();
abstract void reportUnknownTarget();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
index 6340db4..6bc09fd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -8,7 +8,10 @@
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.android.tools.r8.utils.StringUtils;
import java.io.PrintStream;
+import java.util.Set;
class WhyAreYouNotInliningReporterImpl extends WhyAreYouNotInliningReporter {
@@ -45,11 +48,142 @@
}
@Override
+ public void reportBlacklisted() {
+ print("method is blacklisted from inlining.");
+ }
+
+ @Override
+ public void reportCallerNotSameClass() {
+ print("inlinee can only be inlined into methods in the same class.");
+ }
+
+ @Override
+ public void reportCallerNotSameNest() {
+ print("inlinee can only be inlined into methods in the same class (and its nest members).");
+ }
+
+ @Override
+ public void reportCallerNotSamePackage() {
+ print(
+ "inlinee can only be inlined into methods in the same package "
+ + "(declared package private or accesses package private type or member).");
+ }
+
+ @Override
+ public void reportCallerNotSubtype() {
+ print(
+ "inlinee can only be inlined into methods in the same package and methods in subtypes of "
+ + "the inlinee's enclosing class"
+ + "(declared protected or accesses protected type or member).");
+ }
+
+ @Override
+ public void reportClasspathMethod() {
+ print("inlinee is on the classpath.");
+ }
+
+ @Override
+ public void reportInaccessible() {
+ print("inlinee is not accessible from the caller context.");
+ }
+
+ @Override
+ public void reportIncompatibleClassChangeError() {
+ print("invoke may fail with an IncompatibleClassChangeError.");
+ }
+
+ @Override
+ public void reportIncorrectArity(int numberOfArguments, int arity) {
+ print(
+ "number of arguments ("
+ + numberOfArguments
+ + ") does not match arity of method ("
+ + arity
+ + ").");
+ }
+
+ @Override
+ public void reportInlineeDoesNotHaveCode() {
+ print("inlinee does not have code.");
+ }
+
+ @Override
+ public void reportInlineeNotInliningCandidate() {
+ print("unsupported instruction in inlinee.");
+ }
+
+ @Override
+ public void reportInlineeNotProcessed() {
+ print("inlinee not processed yet.");
+ }
+
+ @Override
+ public void reportInlineeNotSimple() {
+ print(
+ "not inlining due to code size heuristic "
+ + "(inlinee may have multiple callers and is not considered trivial).");
+ }
+
+ @Override
+ public void reportInlineeRefersToClassesNotInMainDex() {
+ print(
+ "inlining could increase the main dex size "
+ + "(caller is in main dex and inlinee refers to classes not in main dex).");
+ }
+
+ @Override
+ public void reportInliningAcrossFeatureSplit() {
+ print("cannot inline across feature splits.");
+ }
+
+ @Override
public void reportInstructionBudgetIsExceeded() {
print("caller's instruction budget is exceeded.");
}
@Override
+ public void reportInvalidDoubleInliningCandidate() {
+ print("inlinee is invoked more than once and could not be inlined into all call sites.");
+ }
+
+ @Override
+ public void reportInvalidInliningReason(Reason reason, Set<Reason> validInliningReasons) {
+ print(
+ "not a valid inlining reason (was: "
+ + reason
+ + ", allowed: one of "
+ + StringUtils.join(validInliningReasons, ", ")
+ + ").");
+ }
+
+ @Override
+ public void reportLibraryMethod() {
+ print("inlinee is a library method.");
+ }
+
+ @Override
+ public void reportMarkedAsNeverInline() {
+ print("method is marked by a -neverinline rule.");
+ }
+
+ @Override
+ public void reportMustTriggerClassInitialization() {
+ print(
+ "cannot guarantee that the enclosing class of the inlinee is guaranteed to be class "
+ + "initialized before the first side-effecting instruction in the inlinee.");
+ }
+
+ @Override
+ public void reportNoInliningIntoConstructorsWhenGeneratingClassFiles() {
+ print("inlining into constructors not supported when generating class files.");
+ }
+
+ @Override
+ public void reportPinned() {
+ print("method is kept by a Proguard configuration rule.");
+ }
+
+ @Override
public void reportPotentialExplosionInExceptionalControlFlowResolutionBlocks(
int estimatedNumberOfControlFlowResolutionBlocks, int threshold) {
printWithExceededThreshold(
@@ -59,10 +193,30 @@
threshold);
}
- // TODO(b/142108662): Always report a meaningful reason.
@Override
- public void reportUnknownReason() {
- print(null);
+ public void reportProcessedConcurrently() {
+ print(
+ "could lead to nondeterministic output since the inlinee is being optimized concurrently.");
+ }
+
+ @Override
+ public void reportReceiverDefinitelyNull() {
+ print("the receiver is always null at the call site.");
+ }
+
+ @Override
+ public void reportReceiverMaybeNull() {
+ print("the receiver may be null at the call site.");
+ }
+
+ @Override
+ public void reportRecursiveMethod() {
+ print("recursive calls are not inlined.");
+ }
+
+ @Override
+ public void reportSynchronizedMethod() {
+ print("synchronized methods are not inlined.");
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index 15eda31..7728c1b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -111,8 +111,6 @@
}
}
- // int String#hashCode()
- // int String#length()
// boolean String#isEmpty()
// boolean String#startsWith(String)
// boolean String#endsWith(String)
@@ -120,6 +118,8 @@
// boolean String#equals(String)
// boolean String#equalsIgnoreCase(String)
// boolean String#contentEquals(String)
+ // int String#hashCode()
+ // int String#length()
// int String#indexOf(String)
// int String#indexOf(int)
// int String#lastIndexOf(String)
@@ -128,6 +128,7 @@
// int String#compareToIgnoreCase(String)
// String String#substring(int)
// String String#substring(int, int)
+ // String String#trim()
public void computeTrivialOperationsOnConstString(IRCode code) {
if (!code.metadata().mayHaveConstString()) {
return;
@@ -191,6 +192,23 @@
continue;
}
+ if (invokedMethod == factory.stringMethods.trim) {
+ Value receiver = invoke.getReceiver().getAliasedValue();
+ if (receiver.hasLocalInfo() || receiver.isPhi() || !receiver.definition.isConstString()) {
+ continue;
+ }
+ DexString resultString =
+ factory.createString(receiver.definition.asConstString().getValue().toString().trim());
+ Value newOutValue =
+ code.createValue(
+ TypeLatticeElement.stringClassType(appView, definitelyNotNull()),
+ invoke.getLocalInfo());
+ affectedValues.addAll(invoke.outValue().affectedValues());
+ it.replaceCurrentInstruction(new ConstString(newOutValue, resultString, throwingInfo));
+ numberOfSimplifiedConversions++;
+ continue;
+ }
+
Function<DexString, Integer> operatorWithNoArg = null;
BiFunction<DexString, DexString, Integer> operatorWithString = null;
BiFunction<DexString, Integer, Integer> operatorWithInt = null;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index 60ca8ca..5597bb5 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -35,13 +35,14 @@
import java.util.List;
import org.objectweb.asm.Opcodes;
-public class DesugaredLibraryAPIConversionCfCodeProvider {
+public abstract class DesugaredLibraryAPIConversionCfCodeProvider extends SyntheticCfCodeProvider {
- private static boolean shouldConvert(
- DexType type,
- DesugaredLibraryAPIConverter converter,
- AppView<?> appView,
- DexString methodName) {
+ DesugaredLibraryAPIConversionCfCodeProvider(AppView<?> appView, DexType holder) {
+ super(appView, holder);
+ }
+
+ boolean shouldConvert(
+ DexType type, DesugaredLibraryAPIConverter converter, DexString methodName) {
if (!appView.rewritePrefix.hasRewrittenType(type)) {
return false;
}
@@ -61,7 +62,12 @@
return false;
}
- public static class APIConverterVivifiedWrapperCfCodeProvider extends SyntheticCfCodeProvider {
+ DexType vivifiedTypeFor(DexType type) {
+ return DesugaredLibraryAPIConverter.vivifiedTypeFor(type, appView);
+ }
+
+ public static class APIConverterVivifiedWrapperCfCodeProvider
+ extends DesugaredLibraryAPIConversionCfCodeProvider {
DexField wrapperField;
DexMethod forwardMethod;
@@ -95,13 +101,13 @@
DexType[] newParameters = forwardMethod.proto.parameters.values.clone();
for (DexType param : forwardMethod.proto.parameters.values) {
instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex));
- if (shouldConvert(param, converter, appView, forwardMethod.name)) {
+ if (shouldConvert(param, converter, forwardMethod.name)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
- converter.createConversionMethod(param, param, converter.vivifiedTypeFor(param)),
+ converter.createConversionMethod(param, param, vivifiedTypeFor(param)),
false));
- newParameters[index - 1] = converter.vivifiedTypeFor(param);
+ newParameters[index - 1] = vivifiedTypeFor(param);
}
if (param == factory.longType || param == factory.doubleType) {
stackIndex++;
@@ -113,7 +119,7 @@
DexType returnType = forwardMethod.proto.returnType;
DexType forwardMethodReturnType =
appView.rewritePrefix.hasRewrittenType(returnType)
- ? converter.vivifiedTypeFor(returnType)
+ ? vivifiedTypeFor(returnType)
: returnType;
DexProto newProto = factory.createProto(forwardMethodReturnType, newParameters);
@@ -126,12 +132,12 @@
instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newForwardMethod, false));
}
- if (shouldConvert(returnType, converter, appView, forwardMethod.name)) {
+ if (shouldConvert(returnType, converter, forwardMethod.name)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
converter.createConversionMethod(
- returnType, converter.vivifiedTypeFor(returnType), returnType),
+ returnType, vivifiedTypeFor(returnType), returnType),
false));
}
if (returnType == factory.voidType) {
@@ -143,7 +149,8 @@
}
}
- public static class APIConverterWrapperCfCodeProvider extends SyntheticCfCodeProvider {
+ public static class APIConverterWrapperCfCodeProvider
+ extends DesugaredLibraryAPIConversionCfCodeProvider {
DexField wrapperField;
DexMethod forwardMethod;
@@ -181,11 +188,11 @@
int stackIndex = 1;
for (DexType param : forwardMethod.proto.parameters.values) {
instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex));
- if (shouldConvert(param, converter, appView, forwardMethod.name)) {
+ if (shouldConvert(param, converter, forwardMethod.name)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
- converter.createConversionMethod(param, converter.vivifiedTypeFor(param), param),
+ converter.createConversionMethod(param, vivifiedTypeFor(param), param),
false));
}
if (param == factory.longType || param == factory.doubleType) {
@@ -201,14 +208,14 @@
}
DexType returnType = forwardMethod.proto.returnType;
- if (shouldConvert(returnType, converter, appView, forwardMethod.name)) {
+ if (shouldConvert(returnType, converter, forwardMethod.name)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
converter.createConversionMethod(
- returnType, returnType, converter.vivifiedTypeFor(returnType)),
+ returnType, returnType, vivifiedTypeFor(returnType)),
false));
- returnType = converter.vivifiedTypeFor(returnType);
+ returnType = vivifiedTypeFor(returnType);
}
if (returnType == factory.voidType) {
instructions.add(new CfReturnVoid());
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index d8fc1c6..7af0752 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
@@ -286,6 +287,14 @@
DexString reservedName = strategy.getReservedName(method, holder);
if (reservedName != null) {
state.reserveName(reservedName, method.method);
+ // This is reserving names which after prefix rewriting will actually override a library
+ // method.
+ if (appView.rewritePrefix.hasRewrittenTypeInSignature(method.method.proto)) {
+ state.reserveName(
+ reservedName,
+ DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
+ method.method, method.method.holder, appView));
+ }
}
}
}
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 57ed2d3..1d209ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -12,17 +12,8 @@
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.experimental.graphinfo.AnnotationGraphNode;
-import com.android.tools.r8.experimental.graphinfo.ClassGraphNode;
-import com.android.tools.r8.experimental.graphinfo.FieldGraphNode;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
-import com.android.tools.r8.experimental.graphinfo.GraphEdgeInfo;
-import com.android.tools.r8.experimental.graphinfo.GraphEdgeInfo.EdgeKind;
-import com.android.tools.r8.experimental.graphinfo.GraphNode;
-import com.android.tools.r8.experimental.graphinfo.KeepRuleGraphNode;
-import com.android.tools.r8.experimental.graphinfo.MethodGraphNode;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Descriptor;
@@ -58,24 +49,20 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.shaking.EnqueuerWorklist.Action;
+import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
-import com.android.tools.r8.utils.DequeUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -84,7 +71,6 @@
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import java.lang.reflect.InvocationHandler;
import java.util.ArrayDeque;
-import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
@@ -164,14 +150,6 @@
private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
- // Canonicalization of external graph-nodes and edge info.
- private final Map<DexItem, AnnotationGraphNode> annotationNodes = new IdentityHashMap<>();
- private final Map<DexType, ClassGraphNode> classNodes = new IdentityHashMap<>();
- private final Map<DexMethod, MethodGraphNode> methodNodes = new IdentityHashMap<>();
- private final Map<DexField, FieldGraphNode> fieldNodes = new IdentityHashMap<>();
- private final Map<ProguardKeepRuleBase, KeepRuleGraphNode> ruleNodes = new IdentityHashMap<>();
- private final Map<EdgeKind, GraphEdgeInfo> reasonInfo = new IdentityHashMap<>();
-
/**
* Set of method signatures used in invoke-super instructions that either cannot be resolved or
* resolve to a private method (leading to an IllegalAccessError).
@@ -199,7 +177,7 @@
* Set of types that are mentioned in the program. We at least need an empty abstract class item
* for these.
*/
- private final SetWithReason<DexProgramClass> liveTypes = new SetWithReason<>(this::registerClass);
+ private final SetWithReportedReason<DexProgramClass> liveTypes;
/** Set of live types defined in the library and classpath. Used to avoid duplicate tracing. */
private final Set<DexClass> liveNonProgramTypes = Sets.newIdentityHashSet();
@@ -213,12 +191,10 @@
private final Set<DexType> skippedProtoExtensionTypes = Sets.newIdentityHashSet();
/** Set of annotation types that are instantiated. */
- private final SetWithReason<DexAnnotation> liveAnnotations =
- new SetWithReason<>(this::registerAnnotation);
+ private final SetWithReason<DexAnnotation> liveAnnotations;
/** Set of types that are actually instantiated. These cannot be abstract. */
- private final SetWithReason<DexProgramClass> instantiatedTypes =
- new SetWithReason<>(this::registerClass);
+ private final SetWithReason<DexProgramClass> instantiatedTypes;
/** Set of all types that are instantiated, directly or indirectly, thus may be abstract. */
private final Set<DexProgramClass> directAndIndirectlyInstantiatedTypes =
@@ -229,8 +205,7 @@
* are required so that invokes can find the method. If a method is only a target but not live,
* its implementation may be removed and it may be marked abstract.
*/
- private final SetWithReason<DexEncodedMethod> targetedMethods =
- new SetWithReason<>(this::registerMethod);
+ private final SetWithReason<DexEncodedMethod> targetedMethods;
/**
* Set of program methods that are used as the bootstrap method for an invoke-dynamic instruction.
*/
@@ -245,20 +220,18 @@
private final Set<DexMethod> lambdaMethodsTargetedByInvokeDynamic = Sets.newIdentityHashSet();
/**
* Set of virtual methods that are the immediate target of an invoke-direct.
- * */
+ */
private final Set<DexMethod> virtualMethodsTargetedByInvokeDirect = Sets.newIdentityHashSet();
/**
* Set of methods that belong to live classes and can be reached by invokes. These need to be
* kept.
*/
- private final SetWithReason<DexEncodedMethod> liveMethods =
- new SetWithReason<>(this::registerMethod);
+ private final SetWithReason<DexEncodedMethod> liveMethods;
/**
* Set of fields that belong to live classes and can be reached by invokes. These need to be kept.
*/
- private final SetWithReason<DexEncodedField> liveFields =
- new SetWithReason<>(this::registerField);
+ private final SetWithReason<DexEncodedField> liveFields;
/**
* Set of service types (from META-INF/services/) that may have been instantiated reflectively via
@@ -270,8 +243,7 @@
* Set of interface types for which there may be instantiations, such as lambda expressions or
* explicit keep rules.
*/
- private final SetWithReason<DexProgramClass> instantiatedInterfaceTypes =
- new SetWithReason<>(this::registerInterface);
+ private final SetWithReason<DexProgramClass> instantiatedInterfaceTypes;
/** A queue of items that need processing. Different items trigger different actions. */
private final EnqueuerWorklist workList;
@@ -314,8 +286,6 @@
new IdentityHashMap<>();
private final GraphReporter graphReporter;
- private final GraphConsumer keptGraphConsumer;
- private CollectingGraphConsumer verificationGraphConsumer = null;
Enqueuer(
AppView<? extends AppInfoWithSubtyping> appView,
@@ -326,8 +296,7 @@
this.appInfo = appView.appInfo();
this.appView = appView;
this.forceProguardCompatibility = options.forceProguardCompatibility;
- this.graphReporter = new GraphReporter();
- this.keptGraphConsumer = recordKeptGraph(options, keptGraphConsumer);
+ this.graphReporter = new GraphReporter(appView, keptGraphConsumer);
this.mode = mode;
this.options = options;
this.workList = EnqueuerWorklist.createWorklist(appView);
@@ -335,12 +304,24 @@
if (options.enableGeneratedMessageLiteShrinking && mode.isInitialOrFinalTreeShaking()) {
registerAnalysis(new ProtoEnqueuerExtension(appView));
}
+
+ liveTypes = new SetWithReportedReason<>();
+ liveAnnotations = new SetWithReason<>(graphReporter::registerAnnotation);
+ instantiatedTypes = new SetWithReason<>(graphReporter::registerClass);
+ targetedMethods = new SetWithReason<>(graphReporter::registerMethod);
+ liveMethods = new SetWithReason<>(graphReporter::registerMethod);
+ liveFields = new SetWithReason<>(graphReporter::registerField);
+ instantiatedInterfaceTypes = new SetWithReason<>(graphReporter::registerInterface);
}
public Mode getMode() {
return mode;
}
+ public GraphReporter getGraphReporter() {
+ return graphReporter;
+ }
+
public Enqueuer registerAnalysis(EnqueuerAnalysis analysis) {
this.analyses.add(analysis);
return this;
@@ -444,7 +425,7 @@
if (clazz.isInterface() && !clazz.accessFlags.isAnnotation()) {
markInterfaceAsInstantiated(clazz, witness);
} else {
- workList.enqueueMarkInstantiatedAction(clazz, witness);
+ workList.enqueueMarkInstantiatedAction(clazz, null, witness);
if (clazz.hasDefaultInitializer()) {
DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer();
if (forceProguardCompatibility) {
@@ -471,14 +452,14 @@
pinnedItems.add(item.toReference());
}
- private void markInterfaceAsInstantiated(DexProgramClass clazz, KeepReason reason) {
+ private void markInterfaceAsInstantiated(DexProgramClass clazz, KeepReasonWitness witness) {
assert clazz.isInterface() && !clazz.accessFlags.isAnnotation();
- if (!instantiatedInterfaceTypes.add(clazz, reason)) {
+ if (!instantiatedInterfaceTypes.add(clazz, witness)) {
return;
}
populateInstantiatedTypesCache(clazz);
- markTypeAsLive(clazz, reason);
+ markTypeAsLive(clazz, witness);
}
private void enqueueFirstNonSerializableClassInitializer(
@@ -787,16 +768,17 @@
@Override
public boolean registerNewInstance(DexType type) {
- return registerNewInstance(type, KeepReason.instantiatedIn(currentMethod));
+ return registerNewInstance(type, currentMethod, KeepReason.instantiatedIn(currentMethod));
}
- public boolean registerNewInstance(DexType type, KeepReason keepReason) {
+ public boolean registerNewInstance(
+ DexType type, DexEncodedMethod context, KeepReason keepReason) {
DexProgramClass clazz = getProgramClassOrNull(type);
if (clazz != null) {
if (clazz.isInterface()) {
- markTypeAsLive(clazz, keepReason);
+ markTypeAsLive(clazz, graphReporter.registerClass(clazz, keepReason));
} else {
- markInstantiated(clazz, keepReason);
+ markInstantiated(clazz, context, keepReason);
}
}
return true;
@@ -931,9 +913,9 @@
if (clazz != null) {
KeepReason reason = KeepReason.methodHandleReferencedIn(currentMethod);
if (clazz.isInterface() && !clazz.accessFlags.isAnnotation()) {
- markInterfaceAsInstantiated(clazz, reason);
+ markInterfaceAsInstantiated(clazz, graphReporter.registerClass(clazz, reason));
} else {
- markInstantiated(clazz, reason);
+ markInstantiated(clazz, null, reason);
}
}
}
@@ -1001,7 +983,8 @@
registerInvokeDirect(method, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
break;
case INVOKE_CONSTRUCTOR:
- registerNewInstance(method.holder, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+ registerNewInstance(
+ method.holder, null, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
break;
default:
throw new Unreachable();
@@ -1103,10 +1086,10 @@
markTypeAsLive(
holder,
scopedMethodsForLiveTypes.computeIfAbsent(type, ignore -> new ScopedDexMethodSet()),
- reason);
+ graphReporter.registerClass(holder, reason));
}
- private void markTypeAsLive(DexType type, Function<DexProgramClass, KeepReason> reason) {
+ private void markTypeAsLive(DexType type, Function<DexProgramClass, KeepReasonWitness> reason) {
if (type.isArrayType()) {
markTypeAsLive(type.toBaseType(appView.dexItemFactory()), reason);
return;
@@ -1125,16 +1108,16 @@
reason.apply(holder));
}
- private void markTypeAsLive(DexProgramClass clazz, KeepReason reason) {
+ private void markTypeAsLive(DexProgramClass clazz, KeepReasonWitness witness) {
markTypeAsLive(
clazz,
scopedMethodsForLiveTypes.computeIfAbsent(clazz.type, ignore -> new ScopedDexMethodSet()),
- reason);
+ witness);
}
private void markTypeAsLive(
- DexProgramClass holder, ScopedDexMethodSet seen, KeepReason reasonForType) {
- if (!liveTypes.add(holder, reasonForType)) {
+ DexProgramClass holder, ScopedDexMethodSet seen, KeepReasonWitness witness) {
+ if (!liveTypes.add(holder, witness)) {
return;
}
@@ -1156,7 +1139,6 @@
markTypeAsLive(holder.superType, reason);
}
-
// We cannot remove virtual methods defined earlier in the type hierarchy if it is widening
// access and is defined in an interface:
//
@@ -1197,7 +1179,7 @@
assert holder.accessFlags.isAnnotation();
assert annotations.stream().allMatch(a -> a.annotation.type == holder.type);
annotations.forEach(annotation -> handleAnnotation(holder, annotation));
- }
+ }
rootSet.forEachDependentStaticMember(holder, appView, this::enqueueDependentItem);
compatEnqueueHolderIfDependentNonStaticMember(
@@ -1315,11 +1297,12 @@
private void registerClassInitializer(DexProgramClass definition, KeepReason reason) {
if (definition.hasClassInitializer()) {
- registerMethod(definition.getClassInitializer(), reason);
+ graphReporter.registerMethod(definition.getClassInitializer(), reason);
}
}
- private void markNonStaticDirectMethodAsReachable(DexMethod method, KeepReason reason) {
+ // Package protected due to entry point from worklist.
+ void markNonStaticDirectMethodAsReachable(DexMethod method, KeepReason reason) {
handleInvokeOfDirectTarget(method, reason);
}
@@ -1434,13 +1417,15 @@
* Adds the class to the set of instantiated classes and marks its fields and methods live
* depending on the currently seen invokes and field reads.
*/
- private void processNewlyInstantiatedClass(DexProgramClass clazz, KeepReason reason) {
+ // Package protected due to entry point from worklist.
+ void processNewlyInstantiatedClass(
+ DexProgramClass clazz, DexEncodedMethod context, KeepReason reason) {
assert !clazz.isInterface() || clazz.accessFlags.isAnnotation();
// Notify analyses. This is done even if `clazz` has already been marked as instantiated,
// because each analysis may depend on seeing all the (clazz, reason) pairs. Thus, not doing so
// could lead to nondeterminism.
analyses.forEach(
- analysis -> analysis.processNewlyInstantiatedClass(clazz.asProgramClass(), reason));
+ analysis -> analysis.processNewlyInstantiatedClass(clazz.asProgramClass(), context));
if (!instantiatedTypes.add(clazz, reason)) {
return;
@@ -1452,7 +1437,7 @@
Log.verbose(getClass(), "Class `%s` is instantiated, processing...", clazz);
}
// This class becomes live, so it and all its supertypes become live types.
- markTypeAsLive(clazz, reason);
+ markTypeAsLive(clazz, graphReporter.registerClass(clazz, reason));
// For all methods of the class, if we have seen a call, mark the method live.
// We only do this for virtual calls, as the other ones will be done directly.
transitionMethodsForInstantiatedClass(clazz);
@@ -1566,26 +1551,45 @@
for (DexEncodedMethod method : libraryClass.virtualMethods()) {
// Note: it may be worthwhile to add a resolution cache here. If so, it must till ensure
// that all library override edges are reported to the kept-graph consumer.
- ResolutionResult resolution =
+ ResolutionResult firstResolution =
appView.appInfo().resolveMethod(instantiatedClass, method.method);
- if (resolution.isValidVirtualTarget(options)) {
- resolution.forEachTarget(
- target -> {
- if (!target.isAbstract()) {
- DexClass targetHolder = appView.definitionFor(target.method.holder);
- if (targetHolder != null && targetHolder.isProgramClass()) {
- DexProgramClass programClass = targetHolder.asProgramClass();
- if (shouldMarkLibraryMethodOverrideAsReachable(programClass, target)) {
- target.setLibraryMethodOverride();
- markVirtualMethodAsLive(
- programClass,
- target,
- KeepReason.isLibraryMethod(programClass, libraryClass.type));
- }
+ markResolutionAsLive(libraryClass, firstResolution);
+
+ // Due to API conversion, some overrides can be hidden since they will be rewritten. See
+ // class comment of DesugaredLibraryAPIConverter and vivifiedType logic.
+ // In the first enqueuer phase, the signature has not been desugared, so firstResolution
+ // maintains the library override. In the second enqueuer phase, the signature has been
+ // desugared, and the second resolution maintains the the library override.
+ if (appView.rewritePrefix.hasRewrittenTypeInSignature(method.method.proto)) {
+ DexMethod methodToResolve =
+ DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
+ method.method, method.method.holder, appView);
+ assert methodToResolve != method.method;
+ ResolutionResult secondResolution =
+ appView.appInfo().resolveMethod(instantiatedClass, methodToResolve);
+ markResolutionAsLive(libraryClass, secondResolution);
+ }
+ }
+ }
+
+ private void markResolutionAsLive(DexClass libraryClass, ResolutionResult resolution) {
+ if (resolution.isValidVirtualTarget(options)) {
+ resolution.forEachTarget(
+ target -> {
+ if (!target.isAbstract()) {
+ DexClass targetHolder = appView.definitionFor(target.method.holder);
+ if (targetHolder != null && targetHolder.isProgramClass()) {
+ DexProgramClass programClass = targetHolder.asProgramClass();
+ if (shouldMarkLibraryMethodOverrideAsReachable(programClass, target)) {
+ target.setLibraryMethodOverride();
+ markVirtualMethodAsLive(
+ programClass,
+ target,
+ KeepReason.isLibraryMethod(programClass, libraryClass.type));
}
}
- });
- }
+ }
+ });
}
}
@@ -1709,11 +1713,12 @@
analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
}
- private void markInstantiated(DexProgramClass clazz, KeepReason reason) {
+ private void markInstantiated(
+ DexProgramClass clazz, DexEncodedMethod context, KeepReason reason) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Register new instantiation of `%s`.", clazz);
}
- workList.enqueueMarkInstantiatedAction(clazz, reason);
+ workList.enqueueMarkInstantiatedAction(clazz, context, reason);
}
private void markLambdaInstantiated(DexType itf, DexEncodedMethod method) {
@@ -1785,7 +1790,8 @@
return directAndIndirectlyInstantiatedTypes.contains(clazz);
}
- private void markInstanceFieldAsReachable(DexEncodedField encodedField, KeepReason reason) {
+ // Package protected due to entry point from worklist.
+ void markInstanceFieldAsReachable(DexEncodedField encodedField, KeepReason reason) {
DexField field = encodedField.field;
if (Log.ENABLED) {
Log.verbose(getClass(), "Marking instance field `%s` as reachable.", field);
@@ -1819,8 +1825,8 @@
}
}
- private void markVirtualMethodAsReachable(
- DexMethod method, boolean interfaceInvoke, KeepReason reason) {
+ // Package protected due to entry point from worklist.
+ void markVirtualMethodAsReachable(DexMethod method, boolean interfaceInvoke, KeepReason reason) {
markVirtualMethodAsReachable(method, interfaceInvoke, reason, (x, y) -> true);
}
@@ -1850,7 +1856,7 @@
MarkedResolutionTarget resolution = virtualTargetsMarkedAsReachable.get(method);
if (resolution != null) {
if (!resolution.isUnresolved()) {
- registerMethod(resolution.method, reason);
+ graphReporter.registerMethod(resolution.method, reason);
}
return;
}
@@ -2044,7 +2050,8 @@
}
}
- private void markSuperMethodAsReachable(DexMethod method, DexEncodedMethod from) {
+ // Package protected due to entry point from worklist.
+ void markSuperMethodAsReachable(DexMethod method, DexEncodedMethod from) {
// We have to mark the immediate target of the descriptor as targeted, as otherwise
// the invoke super will fail in the resolution step with a NSM error.
// See <a
@@ -2126,47 +2133,15 @@
return createAppInfo(appInfo);
}
- private GraphConsumer recordKeptGraph(InternalOptions options, GraphConsumer consumer) {
- if (options.testing.verifyKeptGraphInfo) {
- verificationGraphConsumer = new CollectingGraphConsumer(consumer);
- return verificationGraphConsumer;
- }
- return consumer;
- }
-
- private boolean verifyKeptGraph() {
- if (options.testing.verifyKeptGraphInfo) {
- assert verificationGraphConsumer != null;
- for (DexProgramClass liveType : liveTypes.items) {
- assert verifyRootedPath(liveType, verificationGraphConsumer);
+ public boolean verifyKeptGraph() {
+ if (appView.options().testing.verifyKeptGraphInfo) {
+ for (DexProgramClass liveType : liveTypes.getItems()) {
+ assert graphReporter.verifyRootedPath(liveType);
}
}
return true;
}
- private boolean verifyRootedPath(DexProgramClass liveType, CollectingGraphConsumer graph) {
- ClassGraphNode node = getClassGraphNode(liveType.type);
- Set<GraphNode> seen = Sets.newIdentityHashSet();
- Deque<GraphNode> targets = DequeUtils.newArrayDeque(node);
- while (!targets.isEmpty()) {
- GraphNode item = targets.pop();
- if (item instanceof KeepRuleGraphNode) {
- KeepRuleGraphNode rule = (KeepRuleGraphNode) item;
- if (rule.getPreconditions().isEmpty()) {
- return true;
- }
- }
- if (seen.add(item)) {
- Map<GraphNode, Set<GraphEdgeInfo>> sources = graph.getSourcesTargeting(item);
- assert sources != null : "No sources set for " + item;
- assert !sources.isEmpty() : "Empty sources set for " + item;
- targets.addAll(sources.keySet());
- }
- }
- assert false : "No rooted path to " + liveType.type;
- return true;
- }
-
private AppInfoWithLiveness createAppInfo(AppInfoWithSubtyping appInfo) {
ImmutableSortedSet.Builder<DexType> builder =
ImmutableSortedSet.orderedBy(PresortedComparable::slowCompareTo);
@@ -2258,38 +2233,7 @@
numOfLiveItems += (long) liveFields.items.size();
while (!workList.isEmpty()) {
Action action = workList.poll();
- switch (action.kind) {
- case MARK_INSTANTIATED:
- processNewlyInstantiatedClass((DexProgramClass) action.target, action.reason);
- break;
- case MARK_REACHABLE_FIELD:
- markInstanceFieldAsReachable((DexEncodedField) action.target, action.reason);
- break;
- case MARK_REACHABLE_DIRECT:
- markNonStaticDirectMethodAsReachable((DexMethod) action.target, action.reason);
- break;
- case MARK_REACHABLE_VIRTUAL:
- markVirtualMethodAsReachable((DexMethod) action.target, false, action.reason);
- break;
- case MARK_REACHABLE_INTERFACE:
- markVirtualMethodAsReachable((DexMethod) action.target, true, action.reason);
- break;
- case MARK_REACHABLE_SUPER:
- markSuperMethodAsReachable((DexMethod) action.target,
- (DexEncodedMethod) action.context);
- break;
- case MARK_METHOD_KEPT:
- markMethodAsKept((DexEncodedMethod) action.target, action.reason);
- break;
- case MARK_FIELD_KEPT:
- markFieldAsKept((DexEncodedField) action.target, action.reason);
- break;
- case MARK_METHOD_LIVE:
- markMethodAsLive(((DexEncodedMethod) action.target), action.reason);
- break;
- default:
- throw new IllegalArgumentException("" + action.kind);
- }
+ action.run(this);
}
// Continue fix-point processing if -if rules are enabled by items that newly became live.
@@ -2393,7 +2337,8 @@
lambdaMethodsTargetedByInvokeDynamic.clear();
}
- private void markMethodAsKept(DexEncodedMethod target, KeepReason reason) {
+ // Package protected due to entry point from worklist.
+ void markMethodAsKept(DexEncodedMethod target, KeepReason reason) {
DexMethod method = target.method;
DexProgramClass holder = getProgramClassOrNull(method.holder);
if (holder == null) {
@@ -2432,7 +2377,8 @@
}
}
- private void markFieldAsKept(DexEncodedField target, KeepReason reason) {
+ // Package protected due to entry point from worklist.
+ void markFieldAsKept(DexEncodedField target, KeepReason reason) {
DexProgramClass clazz = getProgramClassOrNull(target.field.holder);
if (clazz == null) {
return;
@@ -2485,7 +2431,8 @@
return false;
}
- private void markMethodAsLive(DexEncodedMethod method, KeepReason reason) {
+ // Package protected due to entry point from worklist.
+ void markMethodAsLive(DexEncodedMethod method, KeepReason reason) {
assert liveMethods.contains(method);
DexProgramClass clazz = getProgramClassOrNull(method.method.holder);
@@ -2535,18 +2482,19 @@
}
private void markClassAsInstantiatedWithReason(DexProgramClass clazz, KeepReason reason) {
- workList.enqueueMarkInstantiatedAction(clazz, reason);
+ workList.enqueueMarkInstantiatedAction(clazz, null, reason);
if (clazz.hasDefaultInitializer()) {
workList.enqueueMarkReachableDirectAction(clazz.getDefaultInitializer().method, reason);
}
}
- private void markClassAsInstantiatedWithCompatRule(DexProgramClass clazz, KeepReason reason) {
+ private void markClassAsInstantiatedWithCompatRule(
+ DexProgramClass clazz, KeepReasonWitness witness) {
if (clazz.isInterface() && !clazz.accessFlags.isAnnotation()) {
- markInterfaceAsInstantiated(clazz, reason);
+ markInterfaceAsInstantiated(clazz, witness);
return;
}
- workList.enqueueMarkInstantiatedAction(clazz, reason);
+ workList.enqueueMarkInstantiatedAction(clazz, null, witness);
if (clazz.hasDefaultInitializer()) {
DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer();
workList.enqueueMarkReachableDirectAction(
@@ -2610,7 +2558,7 @@
return;
}
if (!clazz.isInterface()) {
- markInstantiated(clazz, KeepReason.reflectiveUseIn(method));
+ markInstantiated(clazz, null, KeepReason.reflectiveUseIn(method));
if (clazz.hasDefaultInitializer()) {
DexEncodedMethod initializer = clazz.getDefaultInitializer();
KeepReason reason = KeepReason.reflectiveUseIn(method);
@@ -2637,7 +2585,7 @@
!encodedField.accessFlags.isStatic()
&& dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod);
if (keepClass) {
- markInstantiated(clazz, KeepReason.reflectiveUseIn(method));
+ markInstantiated(clazz, null, KeepReason.reflectiveUseIn(method));
}
markFieldAsKept(encodedField, KeepReason.reflectiveUseIn(method));
// Fields accessed by reflection is marked as both read and written.
@@ -2897,6 +2845,24 @@
}
}
+ private static class SetWithReportedReason<T> {
+
+ private final Set<T> items = Sets.newIdentityHashSet();
+
+ boolean add(T item, KeepReasonWitness witness) {
+ assert witness != null;
+ return items.add(item);
+ }
+
+ boolean contains(T item) {
+ return items.contains(item);
+ }
+
+ Set<T> getItems() {
+ return Collections.unmodifiableSet(items);
+ }
+ }
+
private static class SetWithReason<T> {
private final Set<T> items = Sets.newIdentityHashSet();
@@ -2921,7 +2887,7 @@
}
}
- private static class MarkedResolutionTarget {
+ public static class MarkedResolutionTarget {
private static final MarkedResolutionTarget UNRESOLVED = new MarkedResolutionTarget(null, null);
@@ -2957,6 +2923,7 @@
}
private static class ReachableVirtualMethodsSet {
+
private final Map<DexEncodedMethod, Set<MarkedResolutionTarget>> methods =
Maps.newIdentityHashMap();
@@ -3120,402 +3087,4 @@
}
}
- class GraphReporter {
-
- private EdgeKind reportPrecondition(KeepRuleGraphNode keepRuleGraphNode) {
- if (keepRuleGraphNode.getPreconditions().isEmpty()) {
- return EdgeKind.KeepRule;
- }
- for (GraphNode precondition : keepRuleGraphNode.getPreconditions()) {
- reportEdge(precondition, keepRuleGraphNode, EdgeKind.KeepRulePrecondition);
- }
- return EdgeKind.ConditionalKeepRule;
- }
-
- KeepReasonWitness reportKeepClass(
- DexDefinition precondition, ProguardKeepRuleBase rule, DexProgramClass clazz) {
- if (keptGraphConsumer == null) {
- return KeepReasonWitness.INSTANCE;
- }
- KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
- EdgeKind edgeKind = reportPrecondition(ruleNode);
- return reportEdge(ruleNode, getClassGraphNode(clazz.type), edgeKind);
- }
-
- KeepReasonWitness reportKeepClass(
- DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexProgramClass clazz) {
- assert !rules.isEmpty();
- if (keptGraphConsumer != null) {
- for (ProguardKeepRuleBase rule : rules) {
- reportKeepClass(precondition, rule, clazz);
- }
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- KeepReasonWitness reportKeepMethod(
- DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedMethod method) {
- if (keptGraphConsumer == null) {
- return KeepReasonWitness.INSTANCE;
- }
- KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
- EdgeKind edgeKind = reportPrecondition(ruleNode);
- return reportEdge(ruleNode, getMethodGraphNode(method.method), edgeKind);
- }
-
- KeepReasonWitness reportKeepMethod(
- DexDefinition precondition,
- Collection<ProguardKeepRuleBase> rules,
- DexEncodedMethod method) {
- assert !rules.isEmpty();
- if (keptGraphConsumer != null) {
- for (ProguardKeepRuleBase rule : rules) {
- reportKeepMethod(precondition, rule, method);
- }
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- KeepReasonWitness reportKeepField(
- DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedField field) {
- if (keptGraphConsumer == null) {
- return KeepReasonWitness.INSTANCE;
- }
- KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
- EdgeKind edgeKind = reportPrecondition(ruleNode);
- return reportEdge(ruleNode, getFieldGraphNode(field.field), edgeKind);
- }
-
- KeepReasonWitness reportKeepField(
- DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedField field) {
- assert !rules.isEmpty();
- if (keptGraphConsumer != null) {
- for (ProguardKeepRuleBase rule : rules) {
- reportKeepField(precondition, rule, field);
- }
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- public KeepReasonWitness reportCompatKeepDefaultInitializer(
- DexProgramClass holder, DexEncodedMethod defaultInitializer) {
- assert holder.type == defaultInitializer.method.holder;
- assert holder.getDefaultInitializer() == defaultInitializer;
- if (keptGraphConsumer != null) {
- reportEdge(
- getClassGraphNode(holder.type),
- getMethodGraphNode(defaultInitializer.method),
- EdgeKind.CompatibilityRule);
- }
- return KeepReasonWitness.COMPAT_INSTANCE;
- }
-
- public KeepReasonWitness reportCompatKeepMethod(
- DexProgramClass holder, DexEncodedMethod method) {
- assert holder.type == method.method.holder;
- // TODO(b/141729349): This compat rule is from the method to itself and has not edge. Fix it.
- // The rule is stating that if the method is targeted it is live. Since such an edge does
- // not contribute to additional information in the kept graph as it stands (no distinction
- // of targeted vs live edges), there is little point in emitting it.
- return KeepReasonWitness.COMPAT_INSTANCE;
- }
-
- public KeepReason reportCompatInstantiated(
- DexProgramClass instantiated, DexEncodedMethod method) {
- if (keptGraphConsumer != null) {
- reportEdge(
- getMethodGraphNode(method.method),
- getClassGraphNode(instantiated.type),
- EdgeKind.CompatibilityRule);
- }
- return KeepReasonWitness.COMPAT_INSTANCE;
- }
-
- public KeepReasonWitness reportClassReferencedFrom(
- DexProgramClass clazz, DexEncodedMethod method) {
- if (keptGraphConsumer != null) {
- MethodGraphNode source = getMethodGraphNode(method.method);
- ClassGraphNode target = getClassGraphNode(clazz.type);
- return reportEdge(source, target, EdgeKind.ReferencedFrom);
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- public KeepReasonWitness reportClassReferencedFrom(
- DexProgramClass clazz, DexEncodedField field) {
- if (keptGraphConsumer != null) {
- FieldGraphNode source = getFieldGraphNode(field.field);
- ClassGraphNode target = getClassGraphNode(clazz.type);
- return reportEdge(source, target, EdgeKind.ReferencedFrom);
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- public KeepReasonWitness reportReachableMethodAsLive(
- DexEncodedMethod encodedMethod, MarkedResolutionTarget reason) {
- if (keptGraphConsumer != null) {
- return reportEdge(
- getMethodGraphNode(reason.method.method),
- getMethodGraphNode(encodedMethod.method),
- EdgeKind.OverridingMethod);
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- public KeepReasonWitness reportReachableMethodAsLive(
- DexEncodedMethod encodedMethod, Set<MarkedResolutionTarget> reasons) {
- assert !reasons.isEmpty();
- if (keptGraphConsumer != null) {
- MethodGraphNode target = getMethodGraphNode(encodedMethod.method);
- for (MarkedResolutionTarget reason : reasons) {
- reportEdge(getMethodGraphNode(reason.method.method), target, EdgeKind.OverridingMethod);
- }
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- private KeepReason reportCompanionClass(DexProgramClass iface, DexProgramClass companion) {
- assert iface.isInterface();
- assert InterfaceMethodRewriter.isCompanionClassType(companion.type);
- if (keptGraphConsumer == null) {
- return KeepReasonWitness.INSTANCE;
- }
- return reportEdge(
- getClassGraphNode(iface.type),
- getClassGraphNode(companion.type),
- EdgeKind.CompanionClass);
- }
-
- private KeepReason reportCompanionMethod(
- DexEncodedMethod definition, DexEncodedMethod implementation) {
- assert InterfaceMethodRewriter.isCompanionClassType(implementation.method.holder);
- if (keptGraphConsumer == null) {
- return KeepReasonWitness.INSTANCE;
- }
- return reportEdge(
- getMethodGraphNode(definition.method),
- getMethodGraphNode(implementation.method),
- EdgeKind.CompanionMethod);
- }
-
- private KeepReasonWitness reportEdge(
- GraphNode source, GraphNode target, GraphEdgeInfo.EdgeKind kind) {
- assert keptGraphConsumer != null;
- keptGraphConsumer.acceptEdge(source, target, getEdgeInfo(kind));
- return KeepReasonWitness.INSTANCE;
- }
-
- }
-
- /**
- * Sentinel value indicating that a keep reason has been reported.
- *
- * <p>Should only ever be returned by the register* function below.
- */
- private static class KeepReasonWitness extends KeepReason {
-
- private static KeepReasonWitness INSTANCE = new KeepReasonWitness();
- private static KeepReasonWitness COMPAT_INSTANCE =
- new KeepReasonWitness() {
- @Override
- public boolean isDueToProguardCompatibility() {
- return true;
- }
- };
-
- @Override
- public EdgeKind edgeKind() {
- throw new Unreachable();
- }
-
- @Override
- public GraphNode getSourceNode(Enqueuer enqueuer) {
- throw new Unreachable();
- }
- }
-
- private boolean skipReporting(KeepReason reason) {
- assert reason != null;
- if (reason == KeepReasonWitness.INSTANCE || reason == KeepReasonWitness.COMPAT_INSTANCE) {
- return true;
- }
- assert getSourceNode(reason) != null;
- return keptGraphConsumer == null;
- }
-
- private KeepReasonWitness registerType(DexType type, KeepReason reason) {
- if (skipReporting(reason)) {
- return KeepReasonWitness.INSTANCE;
- }
- return registerEdge(getClassGraphNode(type), reason);
- }
-
- private KeepReasonWitness registerInterface(DexProgramClass iface, KeepReason reason) {
- assert iface.isInterface();
- if (skipReporting(reason)) {
- return KeepReasonWitness.INSTANCE;
- }
- return registerEdge(getClassGraphNode(iface.type), reason);
- }
-
- private KeepReasonWitness registerClass(DexProgramClass clazz, KeepReason reason) {
- if (skipReporting(reason)) {
- return KeepReasonWitness.INSTANCE;
- }
- return registerEdge(getClassGraphNode(clazz.type), reason);
- }
-
- private KeepReasonWitness registerAnnotation(DexAnnotation annotation, KeepReason reason) {
- if (skipReporting(reason)) {
- return KeepReasonWitness.INSTANCE;
- }
- return registerEdge(getAnnotationGraphNode(annotation.annotation.type), reason);
- }
-
- private KeepReasonWitness registerMethod(DexEncodedMethod method, KeepReason reason) {
- if (skipReporting(reason)) {
- return KeepReasonWitness.INSTANCE;
- }
- if (reason.edgeKind() == EdgeKind.IsLibraryMethod && isNonProgramClass(method.method.holder)) {
- // Don't report edges to actual library methods.
- // TODO(b/120959039): This should be dead code once no library classes are ever enqueued.
- return KeepReasonWitness.INSTANCE;
- }
- return registerEdge(getMethodGraphNode(method.method), reason);
- }
-
- private KeepReasonWitness registerField(DexEncodedField field, KeepReason reason) {
- if (skipReporting(reason)) {
- return KeepReasonWitness.INSTANCE;
- }
- return registerEdge(getFieldGraphNode(field.field), reason);
- }
-
- private KeepReasonWitness registerEdge(GraphNode target, KeepReason reason) {
- assert !skipReporting(reason);
- GraphNode sourceNode = getSourceNode(reason);
- // TODO(b/120959039): Make sure we do have edges to nodes deriving library nodes!
- if (!sourceNode.isLibraryNode()) {
- GraphEdgeInfo edgeInfo = getEdgeInfo(reason);
- keptGraphConsumer.acceptEdge(sourceNode, target, edgeInfo);
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- private boolean isNonProgramClass(DexType type) {
- DexClass clazz = appView.definitionFor(type);
- return clazz == null || clazz.isNotProgramClass();
- }
-
- private GraphNode getSourceNode(KeepReason reason) {
- return reason.getSourceNode(this);
- }
-
- public GraphNode getGraphNode(DexReference reference) {
- if (reference.isDexType()) {
- return getClassGraphNode(reference.asDexType());
- }
- if (reference.isDexMethod()) {
- return getMethodGraphNode(reference.asDexMethod());
- }
- if (reference.isDexField()) {
- return getFieldGraphNode(reference.asDexField());
- }
- throw new Unreachable();
- }
-
- GraphEdgeInfo getEdgeInfo(KeepReason reason) {
- return getEdgeInfo(reason.edgeKind());
- }
-
- GraphEdgeInfo getEdgeInfo(GraphEdgeInfo.EdgeKind kind) {
- return reasonInfo.computeIfAbsent(kind, k -> new GraphEdgeInfo(k));
- }
-
- AnnotationGraphNode getAnnotationGraphNode(DexItem type) {
- return annotationNodes.computeIfAbsent(type, t -> {
- if (t instanceof DexType) {
- return new AnnotationGraphNode(getClassGraphNode(((DexType) t)));
- }
- throw new Unimplemented("Incomplete support for annotation node on item: " + type.getClass());
- });
- }
-
- ClassGraphNode getClassGraphNode(DexType type) {
- return classNodes.computeIfAbsent(
- type,
- t -> {
- DexClass definition = appView.definitionFor(t);
- return new ClassGraphNode(
- definition != null && definition.isNotProgramClass(),
- Reference.classFromDescriptor(t.toDescriptorString()));
- });
- }
-
- MethodGraphNode getMethodGraphNode(DexMethod context) {
- return methodNodes.computeIfAbsent(
- context,
- m -> {
- DexClass holderDefinition = appView.definitionFor(context.holder);
- Builder<TypeReference> builder = ImmutableList.builder();
- for (DexType param : m.proto.parameters.values) {
- builder.add(Reference.typeFromDescriptor(param.toDescriptorString()));
- }
- return new MethodGraphNode(
- holderDefinition != null && holderDefinition.isNotProgramClass(),
- Reference.method(
- Reference.classFromDescriptor(m.holder.toDescriptorString()),
- m.name.toString(),
- builder.build(),
- m.proto.returnType.isVoidType()
- ? null
- : Reference.typeFromDescriptor(m.proto.returnType.toDescriptorString())));
- });
- }
-
- FieldGraphNode getFieldGraphNode(DexField context) {
- return fieldNodes.computeIfAbsent(
- context,
- f -> {
- DexClass holderDefinition = appView.definitionFor(context.holder);
- return new FieldGraphNode(
- holderDefinition != null && holderDefinition.isNotProgramClass(),
- Reference.field(
- Reference.classFromDescriptor(f.holder.toDescriptorString()),
- f.name.toString(),
- Reference.typeFromDescriptor(f.type.toDescriptorString())));
- });
- }
-
- // Due to the combined encoding of dependent rules, ala keepclassmembers and conditional keep
- // rules the conversion to a keep-rule graph node can be one of three forms:
- // 1. A non-dependent keep rule. In this case precondtion == null and the rule is not an if-rule.
- // 2. A dependent keep rule. In this case precondtion != null and rule is not an if-rule.
- // 3. A conditional keep rule. In this case rule is an if-rule, but precondition may or may not be
- // null. In the non-null case, the precondition is the type the consequent may depend on,
- // say T for the consequent "-keep T { f; }". It is *not* the precondition of the conditional
- // rule.
- KeepRuleGraphNode getKeepRuleGraphNode(DexDefinition precondition, ProguardKeepRuleBase rule) {
- if (rule instanceof ProguardKeepRule) {
- Set<GraphNode> preconditions =
- precondition != null
- ? Collections.singleton(getGraphNode(precondition.toReference()))
- : Collections.emptySet();
- return ruleNodes.computeIfAbsent(rule, key -> new KeepRuleGraphNode(rule, preconditions));
- }
- if (rule instanceof ProguardIfRule) {
- ProguardIfRule ifRule = (ProguardIfRule) rule;
- assert !ifRule.getPreconditions().isEmpty();
- return ruleNodes.computeIfAbsent(
- ifRule,
- key -> {
- Set<GraphNode> preconditions = new HashSet<>(ifRule.getPreconditions().size());
- for (DexReference condition : ifRule.getPreconditions()) {
- preconditions.add(getGraphNode(condition));
- }
- return new KeepRuleGraphNode(ifRule, preconditions);
- });
- }
- throw new Unreachable("Unexpected type of keep rule: " + rule);
- }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index bde6b8d..275e5a7 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -7,7 +7,6 @@
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.DexItem;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import java.util.ArrayDeque;
@@ -15,31 +14,146 @@
public class EnqueuerWorklist {
- public static class Action {
+ public abstract static class Action {
+ public abstract void run(Enqueuer enqueuer);
+ }
- public enum Kind {
- MARK_REACHABLE_DIRECT,
- MARK_REACHABLE_VIRTUAL,
- MARK_REACHABLE_INTERFACE,
- MARK_REACHABLE_SUPER,
- MARK_REACHABLE_FIELD,
- MARK_INSTANTIATED,
- MARK_METHOD_LIVE,
- MARK_METHOD_KEPT,
- MARK_FIELD_KEPT
- }
-
- final Kind kind;
- final DexItem target;
- final DexItem context;
+ static class MarkReachableDirectAction extends Action {
+ final DexMethod target;
final KeepReason reason;
- private Action(Kind kind, DexItem target, DexItem context, KeepReason reason) {
- this.kind = kind;
+ MarkReachableDirectAction(DexMethod target, KeepReason reason) {
+ this.target = target;
+ this.reason = reason;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.markNonStaticDirectMethodAsReachable(target, reason);
+ }
+ }
+
+ static class MarkReachableVirtualAction extends Action {
+ final DexMethod target;
+ final KeepReason reason;
+
+ MarkReachableVirtualAction(DexMethod target, KeepReason reason) {
+ this.target = target;
+ this.reason = reason;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.markVirtualMethodAsReachable(target, false, reason);
+ }
+ }
+
+ static class MarkReachableInterfaceAction extends Action {
+ final DexMethod target;
+ final KeepReason reason;
+
+ public MarkReachableInterfaceAction(DexMethod target, KeepReason reason) {
+ this.target = target;
+ this.reason = reason;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.markVirtualMethodAsReachable(target, true, reason);
+ }
+ }
+
+ static class MarkReachableSuperAction extends Action {
+ final DexMethod target;
+ final DexEncodedMethod context;
+
+ public MarkReachableSuperAction(DexMethod target, DexEncodedMethod context) {
+ this.target = target;
+ this.context = context;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.markSuperMethodAsReachable(target, context);
+ }
+ }
+
+ static class MarkReachableFieldAction extends Action {
+ final DexEncodedField target;
+ final KeepReason reason;
+
+ public MarkReachableFieldAction(DexEncodedField target, KeepReason reason) {
+ this.target = target;
+ this.reason = reason;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.markInstanceFieldAsReachable(target, reason);
+ }
+ }
+
+ static class MarkInstantiatedAction extends Action {
+ final DexProgramClass target;
+ final DexEncodedMethod context;
+ final KeepReason reason;
+
+ public MarkInstantiatedAction(
+ DexProgramClass target, DexEncodedMethod context, KeepReason reason) {
this.target = target;
this.context = context;
this.reason = reason;
}
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.processNewlyInstantiatedClass(target, context, reason);
+ }
+ }
+
+ static class MarkMethodLiveAction extends Action {
+ final DexEncodedMethod target;
+ final KeepReason reason;
+
+ public MarkMethodLiveAction(DexEncodedMethod target, KeepReason reason) {
+ this.target = target;
+ this.reason = reason;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.markMethodAsLive(target, reason);
+ }
+ }
+
+ static class MarkMethodKeptAction extends Action {
+ final DexEncodedMethod target;
+ final KeepReason reason;
+
+ public MarkMethodKeptAction(DexEncodedMethod target, KeepReason reason) {
+ this.target = target;
+ this.reason = reason;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.markMethodAsKept(target, reason);
+ }
+ }
+
+ static class MarkFieldKeptAction extends Action {
+ final DexEncodedField target;
+ final KeepReason reason;
+
+ public MarkFieldKeptAction(DexEncodedField target, KeepReason reason) {
+ this.target = target;
+ this.reason = reason;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.markFieldAsKept(target, reason);
+ }
}
private final AppView<?> appView;
@@ -62,45 +176,48 @@
}
void enqueueMarkReachableDirectAction(DexMethod method, KeepReason reason) {
- queue.add(new Action(Action.Kind.MARK_REACHABLE_DIRECT, method, null, reason));
+ queue.add(new MarkReachableDirectAction(method, reason));
}
void enqueueMarkReachableVirtualAction(DexMethod method, KeepReason reason) {
- queue.add(new Action(Action.Kind.MARK_REACHABLE_VIRTUAL, method, null, reason));
+ queue.add(new MarkReachableVirtualAction(method, reason));
}
void enqueueMarkReachableInterfaceAction(DexMethod method, KeepReason reason) {
- queue.add(new Action(Action.Kind.MARK_REACHABLE_INTERFACE, method, null, reason));
+ queue.add(new MarkReachableInterfaceAction(method, reason));
}
void enqueueMarkReachableSuperAction(DexMethod method, DexEncodedMethod from) {
- queue.add(new Action(Action.Kind.MARK_REACHABLE_SUPER, method, from, null));
+ queue.add(new MarkReachableSuperAction(method, from));
}
public void enqueueMarkReachableFieldAction(
DexProgramClass clazz, DexEncodedField field, KeepReason reason) {
assert field.field.holder == clazz.type;
- queue.add(new Action(Action.Kind.MARK_REACHABLE_FIELD, field, null, reason));
+ queue.add(new MarkReachableFieldAction(field, reason));
}
- void enqueueMarkInstantiatedAction(DexProgramClass clazz, KeepReason reason) {
+ // TODO(b/142378367): Context is the containing method that is cause of the instantiation.
+ // Consider updating call sites with the context information to increase precision where possible.
+ void enqueueMarkInstantiatedAction(
+ DexProgramClass clazz, DexEncodedMethod context, KeepReason reason) {
assert !clazz.isInterface() || clazz.accessFlags.isAnnotation();
- queue.add(new Action(Action.Kind.MARK_INSTANTIATED, clazz, null, reason));
+ queue.add(new MarkInstantiatedAction(clazz, context, reason));
}
void enqueueMarkMethodLiveAction(
DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) {
assert method.method.holder == clazz.type;
- queue.add(new Action(Action.Kind.MARK_METHOD_LIVE, method, null, reason));
+ queue.add(new MarkMethodLiveAction(method, reason));
}
void enqueueMarkMethodKeptAction(DexEncodedMethod method, KeepReason reason) {
assert method.isProgramMethod(appView);
- queue.add(new Action(Action.Kind.MARK_METHOD_KEPT, method, null, reason));
+ queue.add(new MarkMethodKeptAction(method, reason));
}
void enqueueMarkFieldKeptAction(DexEncodedField field, KeepReason reason) {
assert field.isProgramField(appView);
- queue.add(new Action(Action.Kind.MARK_FIELD_KEPT, field, null, reason));
+ queue.add(new MarkFieldKeptAction(field, reason));
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
new file mode 100644
index 0000000..5dc405e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -0,0 +1,475 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.experimental.graphinfo.AnnotationGraphNode;
+import com.android.tools.r8.experimental.graphinfo.ClassGraphNode;
+import com.android.tools.r8.experimental.graphinfo.FieldGraphNode;
+import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.experimental.graphinfo.GraphEdgeInfo;
+import com.android.tools.r8.experimental.graphinfo.GraphEdgeInfo.EdgeKind;
+import com.android.tools.r8.experimental.graphinfo.GraphNode;
+import com.android.tools.r8.experimental.graphinfo.KeepRuleGraphNode;
+import com.android.tools.r8.experimental.graphinfo.MethodGraphNode;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinition;
+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.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.shaking.Enqueuer.MarkedResolutionTarget;
+import com.android.tools.r8.utils.DequeUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class GraphReporter {
+
+ private final AppView<?> appView;
+ private final GraphConsumer keptGraphConsumer;
+ private final CollectingGraphConsumer verificationGraphConsumer;
+
+ // Canonicalization of external graph-nodes and edge info.
+ private final Map<DexItem, AnnotationGraphNode> annotationNodes = new IdentityHashMap<>();
+ private final Map<DexType, ClassGraphNode> classNodes = new IdentityHashMap<>();
+ private final Map<DexMethod, MethodGraphNode> methodNodes = new IdentityHashMap<>();
+ private final Map<DexField, FieldGraphNode> fieldNodes = new IdentityHashMap<>();
+ private final Map<ProguardKeepRuleBase, KeepRuleGraphNode> ruleNodes = new IdentityHashMap<>();
+ private final Map<EdgeKind, GraphEdgeInfo> reasonInfo = new IdentityHashMap<>();
+
+ GraphReporter(AppView<?> appView, GraphConsumer keptGraphConsumer) {
+ this.appView = appView;
+ if (appView.options().testing.verifyKeptGraphInfo) {
+ this.verificationGraphConsumer = new CollectingGraphConsumer(keptGraphConsumer);
+ this.keptGraphConsumer = verificationGraphConsumer;
+ } else {
+ this.verificationGraphConsumer = null;
+ this.keptGraphConsumer = keptGraphConsumer;
+ }
+ }
+
+ public boolean verifyRootedPath(DexProgramClass liveType) {
+ assert verificationGraphConsumer != null;
+ ClassGraphNode node = getClassGraphNode(liveType.type);
+ Set<GraphNode> seen = Sets.newIdentityHashSet();
+ Deque<GraphNode> targets = DequeUtils.newArrayDeque(node);
+ while (!targets.isEmpty()) {
+ GraphNode item = targets.pop();
+ if (item instanceof KeepRuleGraphNode) {
+ KeepRuleGraphNode rule = (KeepRuleGraphNode) item;
+ if (rule.getPreconditions().isEmpty()) {
+ return true;
+ }
+ }
+ if (seen.add(item)) {
+ Map<GraphNode, Set<GraphEdgeInfo>> sources =
+ verificationGraphConsumer.getSourcesTargeting(item);
+ assert sources != null : "No sources set for " + item;
+ assert !sources.isEmpty() : "Empty sources set for " + item;
+ targets.addAll(sources.keySet());
+ }
+ }
+ assert false : "No rooted path to " + liveType.type;
+ return false;
+ }
+
+ private EdgeKind reportPrecondition(KeepRuleGraphNode keepRuleGraphNode) {
+ if (keepRuleGraphNode.getPreconditions().isEmpty()) {
+ return EdgeKind.KeepRule;
+ }
+ for (GraphNode precondition : keepRuleGraphNode.getPreconditions()) {
+ reportEdge(precondition, keepRuleGraphNode, EdgeKind.KeepRulePrecondition);
+ }
+ return EdgeKind.ConditionalKeepRule;
+ }
+
+ KeepReasonWitness reportKeepClass(
+ DexDefinition precondition, ProguardKeepRuleBase rule, DexProgramClass clazz) {
+ if (keptGraphConsumer == null) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
+ EdgeKind edgeKind = reportPrecondition(ruleNode);
+ return reportEdge(ruleNode, getClassGraphNode(clazz.type), edgeKind);
+ }
+
+ KeepReasonWitness reportKeepClass(
+ DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexProgramClass clazz) {
+ assert !rules.isEmpty();
+ if (keptGraphConsumer != null) {
+ for (ProguardKeepRuleBase rule : rules) {
+ reportKeepClass(precondition, rule, clazz);
+ }
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ KeepReasonWitness reportKeepMethod(
+ DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedMethod method) {
+ if (keptGraphConsumer == null) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
+ EdgeKind edgeKind = reportPrecondition(ruleNode);
+ return reportEdge(ruleNode, getMethodGraphNode(method.method), edgeKind);
+ }
+
+ KeepReasonWitness reportKeepMethod(
+ DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedMethod method) {
+ assert !rules.isEmpty();
+ if (keptGraphConsumer != null) {
+ for (ProguardKeepRuleBase rule : rules) {
+ reportKeepMethod(precondition, rule, method);
+ }
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ KeepReasonWitness reportKeepField(
+ DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedField field) {
+ if (keptGraphConsumer == null) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
+ EdgeKind edgeKind = reportPrecondition(ruleNode);
+ return reportEdge(ruleNode, getFieldGraphNode(field.field), edgeKind);
+ }
+
+ KeepReasonWitness reportKeepField(
+ DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedField field) {
+ assert !rules.isEmpty();
+ if (keptGraphConsumer != null) {
+ for (ProguardKeepRuleBase rule : rules) {
+ reportKeepField(precondition, rule, field);
+ }
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ public KeepReasonWitness reportCompatKeepDefaultInitializer(
+ DexProgramClass holder, DexEncodedMethod defaultInitializer) {
+ assert holder.type == defaultInitializer.method.holder;
+ assert holder.getDefaultInitializer() == defaultInitializer;
+ if (keptGraphConsumer != null) {
+ reportEdge(
+ getClassGraphNode(holder.type),
+ getMethodGraphNode(defaultInitializer.method),
+ EdgeKind.CompatibilityRule);
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ public KeepReasonWitness reportCompatKeepMethod(DexProgramClass holder, DexEncodedMethod method) {
+ assert holder.type == method.method.holder;
+ // TODO(b/141729349): This compat rule is from the method to itself and has not edge. Fix it.
+ // The rule is stating that if the method is targeted it is live. Since such an edge does
+ // not contribute to additional information in the kept graph as it stands (no distinction
+ // of targeted vs live edges), there is little point in emitting it.
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ public KeepReasonWitness reportCompatInstantiated(
+ DexProgramClass instantiated, DexEncodedMethod method) {
+ if (keptGraphConsumer != null) {
+ reportEdge(
+ getMethodGraphNode(method.method),
+ getClassGraphNode(instantiated.type),
+ EdgeKind.CompatibilityRule);
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ public KeepReasonWitness reportClassReferencedFrom(
+ DexProgramClass clazz, DexEncodedMethod method) {
+ if (keptGraphConsumer != null) {
+ MethodGraphNode source = getMethodGraphNode(method.method);
+ ClassGraphNode target = getClassGraphNode(clazz.type);
+ return reportEdge(source, target, EdgeKind.ReferencedFrom);
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ public KeepReasonWitness reportClassReferencedFrom(DexProgramClass clazz, DexEncodedField field) {
+ if (keptGraphConsumer != null) {
+ FieldGraphNode source = getFieldGraphNode(field.field);
+ ClassGraphNode target = getClassGraphNode(clazz.type);
+ return reportEdge(source, target, EdgeKind.ReferencedFrom);
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ public KeepReasonWitness reportReachableMethodAsLive(
+ DexEncodedMethod encodedMethod, MarkedResolutionTarget reason) {
+ if (keptGraphConsumer != null) {
+ return reportEdge(
+ getMethodGraphNode(reason.method.method),
+ getMethodGraphNode(encodedMethod.method),
+ EdgeKind.OverridingMethod);
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ public KeepReasonWitness reportReachableMethodAsLive(
+ DexEncodedMethod encodedMethod, Set<MarkedResolutionTarget> reasons) {
+ assert !reasons.isEmpty();
+ if (keptGraphConsumer != null) {
+ MethodGraphNode target = getMethodGraphNode(encodedMethod.method);
+ for (MarkedResolutionTarget reason : reasons) {
+ reportEdge(getMethodGraphNode(reason.method.method), target, EdgeKind.OverridingMethod);
+ }
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ public KeepReasonWitness reportCompanionClass(DexProgramClass iface, DexProgramClass companion) {
+ assert iface.isInterface();
+ assert InterfaceMethodRewriter.isCompanionClassType(companion.type);
+ if (keptGraphConsumer == null) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ return reportEdge(
+ getClassGraphNode(iface.type), getClassGraphNode(companion.type), EdgeKind.CompanionClass);
+ }
+
+ public KeepReasonWitness reportCompanionMethod(
+ DexEncodedMethod definition, DexEncodedMethod implementation) {
+ assert InterfaceMethodRewriter.isCompanionClassType(implementation.method.holder);
+ if (keptGraphConsumer == null) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ return reportEdge(
+ getMethodGraphNode(definition.method),
+ getMethodGraphNode(implementation.method),
+ EdgeKind.CompanionMethod);
+ }
+
+ private KeepReasonWitness reportEdge(GraphNode source, GraphNode target, EdgeKind kind) {
+ assert keptGraphConsumer != null;
+ keptGraphConsumer.acceptEdge(source, target, getEdgeInfo(kind));
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ /**
+ * Sentinel value indicating that a keep reason has been reported.
+ *
+ * <p>Should only ever be returned by the graph reporter functions.
+ */
+ public static class KeepReasonWitness extends KeepReason {
+
+ private static KeepReasonWitness INSTANCE = new KeepReasonWitness();
+
+ private KeepReasonWitness() {
+ // Only the reporter may create instances.
+ }
+
+ @Override
+ public EdgeKind edgeKind() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public GraphNode getSourceNode(GraphReporter graphReporter) {
+ throw new Unreachable();
+ }
+ }
+
+ private boolean skipReporting(KeepReason reason) {
+ assert reason != null;
+ if (reason == KeepReasonWitness.INSTANCE) {
+ return true;
+ }
+ assert getSourceNode(reason) != null;
+ return keptGraphConsumer == null;
+ }
+
+ public KeepReasonWitness registerInterface(DexProgramClass iface, KeepReason reason) {
+ assert iface.isInterface();
+ if (skipReporting(reason)) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ return registerEdge(getClassGraphNode(iface.type), reason);
+ }
+
+ public KeepReasonWitness registerClass(DexProgramClass clazz, KeepReason reason) {
+ if (skipReporting(reason)) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ return registerEdge(getClassGraphNode(clazz.type), reason);
+ }
+
+ public KeepReasonWitness registerAnnotation(DexAnnotation annotation, KeepReason reason) {
+ if (skipReporting(reason)) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ return registerEdge(getAnnotationGraphNode(annotation.annotation.type), reason);
+ }
+
+ public KeepReasonWitness registerMethod(DexEncodedMethod method, KeepReason reason) {
+ if (skipReporting(reason)) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ if (reason.edgeKind() == EdgeKind.IsLibraryMethod && isNonProgramClass(method.method.holder)) {
+ // Don't report edges to actual library methods.
+ // TODO(b/120959039): This should be dead code once no library classes are ever enqueued.
+ return KeepReasonWitness.INSTANCE;
+ }
+ return registerEdge(getMethodGraphNode(method.method), reason);
+ }
+
+ public KeepReasonWitness registerField(DexEncodedField field, KeepReason reason) {
+ if (skipReporting(reason)) {
+ return KeepReasonWitness.INSTANCE;
+ }
+ return registerEdge(getFieldGraphNode(field.field), reason);
+ }
+
+ private KeepReasonWitness registerEdge(GraphNode target, KeepReason reason) {
+ assert !skipReporting(reason);
+ GraphNode sourceNode = getSourceNode(reason);
+ // TODO(b/120959039): Make sure we do have edges to nodes deriving library nodes!
+ if (!sourceNode.isLibraryNode()) {
+ GraphEdgeInfo edgeInfo = getEdgeInfo(reason);
+ keptGraphConsumer.acceptEdge(sourceNode, target, edgeInfo);
+ }
+ return KeepReasonWitness.INSTANCE;
+ }
+
+ private boolean isNonProgramClass(DexType type) {
+ DexClass clazz = appView.definitionFor(type);
+ return clazz == null || clazz.isNotProgramClass();
+ }
+
+ private GraphNode getSourceNode(KeepReason reason) {
+ return reason.getSourceNode(this);
+ }
+
+ public GraphNode getGraphNode(DexReference reference) {
+ if (reference.isDexType()) {
+ return getClassGraphNode(reference.asDexType());
+ }
+ if (reference.isDexMethod()) {
+ return getMethodGraphNode(reference.asDexMethod());
+ }
+ if (reference.isDexField()) {
+ return getFieldGraphNode(reference.asDexField());
+ }
+ throw new Unreachable();
+ }
+
+ GraphEdgeInfo getEdgeInfo(KeepReason reason) {
+ return getEdgeInfo(reason.edgeKind());
+ }
+
+ GraphEdgeInfo getEdgeInfo(EdgeKind kind) {
+ return reasonInfo.computeIfAbsent(kind, k -> new GraphEdgeInfo(k));
+ }
+
+ AnnotationGraphNode getAnnotationGraphNode(DexItem type) {
+ return annotationNodes.computeIfAbsent(
+ type,
+ t -> {
+ if (t instanceof DexType) {
+ return new AnnotationGraphNode(getClassGraphNode(((DexType) t)));
+ }
+ throw new Unimplemented(
+ "Incomplete support for annotation node on item: " + type.getClass());
+ });
+ }
+
+ ClassGraphNode getClassGraphNode(DexType type) {
+ return classNodes.computeIfAbsent(
+ type,
+ t -> {
+ DexClass definition = appView.definitionFor(t);
+ return new ClassGraphNode(
+ definition != null && definition.isNotProgramClass(),
+ Reference.classFromDescriptor(t.toDescriptorString()));
+ });
+ }
+
+ MethodGraphNode getMethodGraphNode(DexMethod context) {
+ return methodNodes.computeIfAbsent(
+ context,
+ m -> {
+ DexClass holderDefinition = appView.definitionFor(context.holder);
+ Builder<TypeReference> builder = ImmutableList.builder();
+ for (DexType param : m.proto.parameters.values) {
+ builder.add(Reference.typeFromDescriptor(param.toDescriptorString()));
+ }
+ return new MethodGraphNode(
+ holderDefinition != null && holderDefinition.isNotProgramClass(),
+ Reference.method(
+ Reference.classFromDescriptor(m.holder.toDescriptorString()),
+ m.name.toString(),
+ builder.build(),
+ m.proto.returnType.isVoidType()
+ ? null
+ : Reference.typeFromDescriptor(m.proto.returnType.toDescriptorString())));
+ });
+ }
+
+ FieldGraphNode getFieldGraphNode(DexField context) {
+ return fieldNodes.computeIfAbsent(
+ context,
+ f -> {
+ DexClass holderDefinition = appView.definitionFor(context.holder);
+ return new FieldGraphNode(
+ holderDefinition != null && holderDefinition.isNotProgramClass(),
+ Reference.field(
+ Reference.classFromDescriptor(f.holder.toDescriptorString()),
+ f.name.toString(),
+ Reference.typeFromDescriptor(f.type.toDescriptorString())));
+ });
+ }
+
+ // Due to the combined encoding of dependent rules, ala keepclassmembers and conditional keep
+ // rules the conversion to a keep-rule graph node can be one of three forms:
+ // 1. A non-dependent keep rule. In this case precondtion == null and the rule is not an if-rule.
+ // 2. A dependent keep rule. In this case precondtion != null and rule is not an if-rule.
+ // 3. A conditional keep rule. In this case rule is an if-rule, but precondition may or may not be
+ // null. In the non-null case, the precondition is the type the consequent may depend on,
+ // say T for the consequent "-keep T { f; }". It is *not* the precondition of the conditional
+ // rule.
+ KeepRuleGraphNode getKeepRuleGraphNode(DexDefinition precondition, ProguardKeepRuleBase rule) {
+ if (rule instanceof ProguardKeepRule) {
+ Set<GraphNode> preconditions =
+ precondition != null
+ ? Collections.singleton(getGraphNode(precondition.toReference()))
+ : Collections.emptySet();
+ return ruleNodes.computeIfAbsent(rule, key -> new KeepRuleGraphNode(rule, preconditions));
+ }
+ if (rule instanceof ProguardIfRule) {
+ ProguardIfRule ifRule = (ProguardIfRule) rule;
+ assert !ifRule.getPreconditions().isEmpty();
+ return ruleNodes.computeIfAbsent(
+ ifRule,
+ key -> {
+ Set<GraphNode> preconditions = new HashSet<>(ifRule.getPreconditions().size());
+ for (DexReference condition : ifRule.getPreconditions()) {
+ preconditions.add(getGraphNode(condition));
+ }
+ return new KeepRuleGraphNode(ifRule, preconditions);
+ });
+ }
+ throw new Unreachable("Unexpected type of keep rule: " + rule);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index 43b875a..680c0b5 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -18,7 +18,7 @@
public abstract GraphEdgeInfo.EdgeKind edgeKind();
- public abstract GraphNode getSourceNode(Enqueuer enqueuer);
+ public abstract GraphNode getSourceNode(GraphReporter graphReporter);
static KeepReason annotatedOn(DexDefinition definition) {
return new AnnotatedOn(definition);
@@ -64,18 +64,6 @@
return false;
}
- public boolean isDueToProguardCompatibility() {
- return false;
- }
-
- public boolean isInstantiatedIn() {
- return false;
- }
-
- public InstatiatedIn asInstantiatedIn() {
- return null;
- }
-
public static KeepReason targetedBySuperFrom(DexEncodedMethod from) {
return new TargetedBySuper(from);
}
@@ -103,8 +91,8 @@
}
@Override
- public GraphNode getSourceNode(Enqueuer enqueuer) {
- return enqueuer.getMethodGraphNode(method.method);
+ public GraphNode getSourceNode(GraphReporter graphReporter) {
+ return graphReporter.getMethodGraphNode(method.method);
}
}
@@ -115,16 +103,6 @@
}
@Override
- public boolean isInstantiatedIn() {
- return true;
- }
-
- @Override
- public InstatiatedIn asInstantiatedIn() {
- return this;
- }
-
- @Override
public EdgeKind edgeKind() {
return EdgeKind.InstantiatedIn;
}
@@ -235,8 +213,8 @@
}
@Override
- public GraphNode getSourceNode(Enqueuer enqueuer) {
- return enqueuer.getClassGraphNode(type);
+ public GraphNode getSourceNode(GraphReporter graphReporter) {
+ return graphReporter.getClassGraphNode(type);
}
}
@@ -256,8 +234,8 @@
}
@Override
- public GraphNode getSourceNode(Enqueuer enqueuer) {
- return enqueuer.getClassGraphNode(implementer);
+ public GraphNode getSourceNode(GraphReporter graphReporter) {
+ return graphReporter.getClassGraphNode(implementer);
}
}
@@ -275,8 +253,8 @@
}
@Override
- public GraphNode getSourceNode(Enqueuer enqueuer) {
- return enqueuer.getAnnotationGraphNode(holder);
+ public GraphNode getSourceNode(GraphReporter graphReporter) {
+ return graphReporter.getAnnotationGraphNode(holder);
}
}
@@ -294,14 +272,14 @@
}
@Override
- public GraphNode getSourceNode(Enqueuer enqueuer) {
+ public GraphNode getSourceNode(GraphReporter graphReporter) {
if (holder.isDexClass()) {
- return enqueuer.getClassGraphNode(holder.asDexClass().type);
+ return graphReporter.getClassGraphNode(holder.asDexClass().type);
} else if (holder.isDexEncodedField()) {
- return enqueuer.getFieldGraphNode(holder.asDexEncodedField().field);
+ return graphReporter.getFieldGraphNode(holder.asDexEncodedField().field);
} else {
assert holder.isDexEncodedMethod();
- return enqueuer.getMethodGraphNode(holder.asDexEncodedMethod().method);
+ return graphReporter.getMethodGraphNode(holder.asDexEncodedMethod().method);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 1347279..ae63272 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -525,19 +525,6 @@
"Missing parameter", handler -> parse(handler, "--output"));
}
- @Test
- public void warnForSpecialLibraryConfiguration() throws Throwable {
- Path emptyZip = temp.getRoot().toPath().resolve("empty.zip");
- DiagnosticsChecker.checkWarningsContains(
- "Desugared library configuration is still work in progress",
- handler ->
- D8Command.builder(handler)
- .addDesugaredLibraryConfiguration(
- StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
- .setOutput(emptyZip, OutputMode.DexIndexed)
- .build());
- }
-
private D8Command parse(String... args) throws CompilationFailedException {
return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 2370b74..7a72d1b 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -114,18 +114,6 @@
}
@Test
- public void warnForSpecialLibraryConfiguration() throws Throwable {
- DiagnosticsChecker.checkWarningsContains(
- "Desugared library configuration is still work in progress",
- handler ->
- prepareBuilder(handler)
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
- .addDesugaredLibraryConfiguration(
- StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
- .build());
- }
-
- @Test
public void addProguardConfigurationString() throws Throwable {
String keepRule = "-keep class java.time.*";
List<String> keepRules = new ArrayList<>();
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 9a6752f..e64a95f 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -199,12 +199,12 @@
return Stream.of();
}
AndroidApiLevel vmLevel = runtime.asDex().getMinApiLevel();
- AndroidApiLevel lowestApplicable = sortedApiLevels.get(sortedApiLevels.size() - 1);
+ AndroidApiLevel lowestApplicable = sortedApiLevels.get(0);
if (vmLevel.getLevel() < lowestApplicable.getLevel()) {
return Stream.of();
}
if (sortedApiLevels.size() > 1) {
- for (int i = 0; i < sortedApiLevels.size(); i++) {
+ for (int i = sortedApiLevels.size() - 1; i >= 0; i--) {
AndroidApiLevel highestApplicable = sortedApiLevels.get(i);
if (highestApplicable.getLevel() <= vmLevel.getLevel()
&& lowestApplicable != highestApplicable) {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index ef32ed5..0464ccb 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -632,7 +632,7 @@
Assert.fail(ioe.toString());
}
CRC32 crc = new CRC32();
- crc.update(bytes);
+ crc.update(bytes, 0, bytes.length);
return crc.getValue();
}
diff --git a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
index 6c64801..231f2c1 100644
--- a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
+++ b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -77,6 +78,7 @@
@Test
public void testFullMode() throws Exception {
+ assumeFalse(parameters.getApiLevel().getLevel() == 28);
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
.addProgramClasses(TestClass.class)
@@ -91,6 +93,7 @@
@Test
public void testCompatibilityMode() throws Exception {
+ assumeFalse(parameters.getApiLevel().getLevel() == 28);
testForR8Compat(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
.addProgramClasses(TestClass.class)
@@ -117,6 +120,7 @@
@Test
public void testFullModeError() {
+ assumeFalse(parameters.getApiLevel().getLevel() == 28);
assumeTrue("Only run for Dex backend", parameters.isDexRuntime());
try {
testForR8(parameters.getBackend())
@@ -134,6 +138,7 @@
@Test
public void testCompatibilityModeWarning() throws Exception {
+ assumeFalse(parameters.getApiLevel().getLevel() == 28);
assumeTrue("Only run for Dex backend", parameters.isDexRuntime());
R8TestCompileResult result =
testForR8Compat(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/CustomCollectionTest.java
index c8a05f8..9a8bb6b 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/CustomCollectionTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -26,6 +27,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -53,6 +55,10 @@
@Test
public void testCustomCollectionD8() throws Exception {
+ // TODO(b/142377475).
+ Assume.assumeTrue(!shrinkDesugaredLibrary);
+ // TODO(b/142377161).
+ Assume.assumeTrue(parameters.getRuntime().asDex().getVm().isNewerThan(DexVm.ART_4_4_4_HOST));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
D8TestRunResult d8TestRunResult =
testForD8()
@@ -83,6 +89,8 @@
@Test
public void testCustomCollectionR8() throws Exception {
+ // TODO(b/142377161).
+ Assume.assumeTrue(parameters.getRuntime().asDex().getVm().isNewerThan(DexVm.ART_4_4_4_HOST));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
R8TestRunResult r8TestRunResult =
testForR8(Backend.DEX)
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/DisableDesugarTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/DisableDesugarTest.java
index ca2ff2b..cb928cc 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/DisableDesugarTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/DisableDesugarTest.java
@@ -34,10 +34,7 @@
private void checkExpectedDiagnostics(TestDiagnosticMessages messages) {
messages.assertInfosCount(0);
- messages.assertWarningsCount(1);
- assertThat(
- messages.getWarnings().get(0).getDiagnosticMessage(),
- containsString("Desugared library configuration is still work in progress"));
+ messages.assertWarningsCount(0);
messages.assertErrorsCount(1);
assertThat(
messages.getErrors().get(0).getDiagnosticMessage(),
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilFunctionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilFunctionTest.java
index 30fd9d3..2ddacd7 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilFunctionTest.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -65,6 +67,7 @@
.addInnerClasses(JavaUtilFunctionTest.class)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setIncludeClassesChecksum(true)
.compile()
.inspect(this::checkRewrittenArguments)
.addDesugaredCoreLibraryRunClassPath(
@@ -100,6 +103,40 @@
.assertSuccessWithOutput(expectedOutput);
}
+ @Test
+ public void testWrapperWithChecksum() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addInnerClasses(JavaUtilFunctionTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setIncludeClassesChecksum(true) // Compilation fails if some classes are missing checksum.
+ .compile()
+ .inspect(
+ inspector -> {
+ assertEquals(
+ parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel() ? 0 : 1,
+ inspector.allClasses().stream()
+ .filter(
+ clazz ->
+ clazz
+ .getFinalName()
+ .contains(DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX))
+ .count());
+ assertEquals(
+ parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel() ? 0 : 1,
+ inspector.allClasses().stream()
+ .filter(
+ clazz ->
+ clazz
+ .getFinalName()
+ .contains(
+ DesugaredLibraryWrapperSynthesizer
+ .VIVIFIED_TYPE_WRAPPER_SUFFIX))
+ .count());
+ });
+ }
+
static class TestClass {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionFinalWarningTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionFinalWarningTest.java
index 10b6d75..48d836d 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionFinalWarningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionFinalWarningTest.java
@@ -25,7 +25,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
- .assertWarningMessageThatMatches(
+ .assertInfoMessageThatMatches(
startsWith(
"Desugared library API conversion: cannot wrap final methods"
+ " [java.util.LongSummaryStatistics"))
@@ -36,7 +36,7 @@
.assertSuccessWithOutput(
StringUtils.lines(
"Unsupported conversion for java.util.LongSummaryStatistics. See compilation time"
- + " warnings for more infos."));
+ + " infos for more details."));
}
static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionLargeWarningTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionLargeWarningTest.java
index efbe9b6..1818802 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionLargeWarningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionLargeWarningTest.java
@@ -24,14 +24,14 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
- .assertWarningMessageThatMatches(
+ .assertInfoMessageThatMatches(
startsWith(
"Desugared library API conversion: Generating a large wrapper for"
+ " java.util.stream.Stream"))
- .assertNoWarningMessageThatMatches(
+ .assertNoInfoMessageThatMatches(
startsWith(
"Desugared library API conversion: Generating a large wrapper for java.time.Clock"))
- .assertNoWarningMessageThatMatches(
+ .assertNoInfoMessageThatMatches(
startsWith(
"Desugared library API conversion: Generating a large wrapper for"
+ " java.util.function.Function"));
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java
index e3e8c77..6bbf8a1 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java
@@ -65,9 +65,9 @@
.assertSuccessWithOutput(
StringUtils.lines(
"[5, 6, 7]",
- "j$.util.stream.IntStream$-V-WRP",
+ "$r8$wrapper$java$util$stream$IntStream$-V-WRP",
"Unsupported conversion for java.util.IntSummaryStatistics. See compilation time"
- + " warnings for more infos."));
+ + " infos for more details."));
}
static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/CallBackConversionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/CallBackConversionTest.java
index f42a03d..49ee9af 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/CallBackConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/CallBackConversionTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import java.nio.file.Path;
import java.util.List;
@@ -63,6 +64,61 @@
.assertSuccessWithOutput(StringUtils.lines("0", "1", "0", "1"));
}
+ @Test
+ public void testCallBackR8() throws Exception {
+ Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+ testForR8(Backend.DEX)
+ .addKeepMainRule(Impl.class)
+ .noMinification()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(Impl.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .compile()
+ .inspect(this::assertLibraryOverridesThere)
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addRunClasspathFiles(customLib)
+ .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Impl.class)
+ .assertSuccessWithOutput(StringUtils.lines("0", "1", "0", "1"));
+ }
+
+ @Test
+ public void testCallBackR8Minifying() throws Exception {
+ Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+ testForR8(Backend.DEX)
+ .addKeepMainRule(Impl.class)
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(Impl.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .compile()
+ .inspect(this::assertLibraryOverridesThere)
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addRunClasspathFiles(customLib)
+ .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Impl.class)
+ .assertSuccessWithOutput(StringUtils.lines("0", "1", "0", "1"));
+ }
+
+ private void assertLibraryOverridesThere(CodeInspector i) {
+ // The j$ method can be optimized, but the java method should be present to be called
+ // through the library.
+ List<FoundMethodSubject> virtualMethods = i.clazz(Impl.class).virtualMethods();
+ assertTrue(
+ virtualMethods.stream()
+ .anyMatch(
+ m ->
+ m.getMethod().method.name.toString().equals("foo")
+ && m.getMethod()
+ .method
+ .proto
+ .parameters
+ .values[0]
+ .toString()
+ .equals("java.util.function.Consumer")));
+ }
+
static class Impl extends CustomLibClass {
public int foo(Consumer<Object> o) {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/MoreFunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/MoreFunctionConversionTest.java
new file mode 100644
index 0000000..80a61e7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/MoreFunctionConversionTest.java
@@ -0,0 +1,142 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.corelib.conversionTests;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.Sets;
+import java.nio.file.Path;
+import java.util.Set;
+import java.util.function.Function;
+import org.junit.Test;
+
+public class MoreFunctionConversionTest extends APIConversionTestBase {
+
+ @Test
+ public void testFunction() throws Exception {
+ Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+ D8TestCompileResult compileResult =
+ testForD8()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .compile();
+ Path program = compileResult.writeToZip();
+ assertNoDuplicateLambdas(program, customLib);
+ compileResult
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addRunClasspathFiles(customLib)
+ .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
+ // TODO(clement): Make output reliable and put back expected results.
+ .assertSuccess();
+ }
+
+ // If we have the exact same lambda in both, but one implements j$..Function and the other
+ // java..Function, ART is obviously very confused.
+ private void assertNoDuplicateLambdas(Path program, Path customLib) throws Exception {
+ CodeInspector programInspector = new CodeInspector(program);
+ CodeInspector customLibInspector = new CodeInspector(customLib);
+ int programSize = programInspector.allClasses().size();
+ int customLibSize = customLibInspector.allClasses().size();
+ Set<String> foundClassSubjects = Sets.newHashSet();
+ for (FoundClassSubject aClass : programInspector.allClasses()) {
+ foundClassSubjects.add(aClass.getOriginalName());
+ }
+ for (FoundClassSubject aClass : customLibInspector.allClasses()) {
+ foundClassSubjects.add(aClass.getOriginalName());
+ }
+ assertEquals(foundClassSubjects.size(), programSize + customLibSize);
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ static class Executor {
+
+ public static void main(String[] args) {
+ returnOnly();
+ oneParameter();
+ twoParameters();
+ oneParameterReturn();
+ twoParametersReturn();
+ }
+
+ public static void returnOnly() {
+ Function<Object, Integer> returnOnly = CustomLibClass.returnOnly();
+ System.out.println(returnOnly.apply(new Object()));
+ }
+
+ public static void oneParameterReturn() {
+ Function<Object, String> toString = Object::toString;
+ Function<Object, Integer> oneParam = CustomLibClass.oneParameterReturn(toString);
+ System.out.println(oneParam.apply(new Object()));
+ }
+
+ public static void twoParametersReturn() {
+ Function<Object, String> toString = Object::toString;
+ Function<String, Integer> length = String::length;
+ Function<Object, Integer> twoParam = CustomLibClass.twoParametersReturn(toString, length);
+ System.out.println(twoParam.apply(new Object()));
+ }
+
+ public static void oneParameter() {
+ Function<Object, String> toString = Object::toString;
+ int res = CustomLibClass.oneParameter(toString);
+ System.out.println(res);
+ }
+
+ public static void twoParameters() {
+ Function<Object, String> toString = Object::toString;
+ Function<String, Integer> length = String::length;
+ int res = CustomLibClass.twoParameters(toString, length);
+ System.out.println(res);
+ }
+ }
+
+ // This class will be put at compilation time as library and on the runtime class path.
+ // This class is convenient for easy testing. Each method plays the role of methods in the
+ // platform APIs for which argument/return values need conversion.
+ @SuppressWarnings("WeakerAccess")
+ static class CustomLibClass {
+
+ public static Function<Object, Integer> returnOnly() {
+ Function<Object, String> toString = getObjectStringConv();
+ return toString.andThen(getStringIntConv());
+ }
+
+ public static Function<Object, Integer> oneParameterReturn(Function<Object, String> f1) {
+ return f1.andThen(getStringIntConv());
+ }
+
+ public static Function<Object, Integer> twoParametersReturn(
+ Function<Object, String> f1, Function<String, Integer> f2) {
+ return f1.andThen(f2);
+ }
+
+ public static int oneParameter(Function<Object, String> f1) {
+ return f1.andThen(getStringIntConv()).apply(new Object());
+ }
+
+ public static int twoParameters(Function<Object, String> f1, Function<String, Integer> f2) {
+ return f1.andThen(f2).apply(new Object());
+ }
+
+ // Following functions are defined to avoid name collision with the program.
+ public static Function<String, Integer> getStringIntConv() {
+ return (String s) -> 1 + s.length() - 1;
+ }
+
+ public static Function<Object, String> getObjectStringConv() {
+ return (Object o) -> "" + o.toString() + "";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TryCatchTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TryCatchTimeConversionTest.java
new file mode 100644
index 0000000..7fdae41
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TryCatchTimeConversionTest.java
@@ -0,0 +1,211 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.corelib.conversionTests;
+
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.time.ZoneId;
+import org.junit.Test;
+
+public class TryCatchTimeConversionTest extends APIConversionTestBase {
+
+ @Test
+ public void testBaseline() throws Exception {
+ Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+ testForD8()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(BaselineExecutor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addRunClasspathFiles(customLib)
+ .run(new DexRuntime(DexVm.ART_9_0_0_HOST), BaselineExecutor.class)
+ .assertSuccessWithOutput(StringUtils.lines("GMT", "GMT", "GMT", "GMT", "GMT"));
+ }
+
+ @Test
+ public void testTryCatch() throws Exception {
+ Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+ testForD8()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(TryCatchExecutor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addRunClasspathFiles(customLib)
+ .run(new DexRuntime(DexVm.ART_9_0_0_HOST), TryCatchExecutor.class)
+ .assertSuccessWithOutput(
+ StringUtils.lines("GMT", "GMT", "GMT", "GMT", "GMT", "Exception caught"));
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ static class BaselineExecutor {
+
+ private static final String ZONE_ID = "GMT";
+
+ public static void main(String[] args) {
+ returnOnly();
+ oneParameter();
+ twoParameters();
+ oneParameterReturn();
+ twoParametersReturn();
+ }
+
+ public static void returnOnly() {
+ ZoneId returnOnly = CustomLibClass.returnOnly();
+ System.out.println(returnOnly.getId());
+ }
+
+ public static void oneParameterReturn() {
+ ZoneId z1 = ZoneId.of(ZONE_ID);
+ ZoneId oneParam = CustomLibClass.oneParameterReturn(z1);
+ System.out.println(oneParam.getId());
+ }
+
+ public static void twoParametersReturn() {
+ ZoneId z1 = ZoneId.of(ZONE_ID);
+ ZoneId z2 = ZoneId.of(ZONE_ID);
+ ZoneId twoParam = CustomLibClass.twoParametersReturn(z1, z2);
+ System.out.println(twoParam.getId());
+ }
+
+ public static void oneParameter() {
+ ZoneId z1 = ZoneId.of(ZONE_ID);
+ String res = CustomLibClass.oneParameter(z1);
+ System.out.println(res);
+ }
+
+ public static void twoParameters() {
+ ZoneId z1 = ZoneId.of(ZONE_ID);
+ ZoneId z2 = ZoneId.of(ZONE_ID);
+ String res = CustomLibClass.twoParameters(z1, z2);
+ System.out.println(res);
+ }
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ static class TryCatchExecutor {
+
+ private static final String ZONE_ID = "GMT";
+
+ public static void main(String[] args) {
+ returnOnly();
+ oneParameter();
+ twoParameters();
+ oneParameterReturn();
+ twoParametersReturn();
+ twoParametersThrow();
+ }
+
+ public static void returnOnly() {
+ ZoneId returnOnly;
+ try {
+ returnOnly = CustomLibClass.returnOnly();
+ } catch (Exception e) {
+ throw new RuntimeException("Test failed.");
+ }
+ System.out.println(returnOnly.getId());
+ }
+
+ public static void oneParameterReturn() {
+ ZoneId z1 = ZoneId.of(ZONE_ID);
+ ZoneId oneParam;
+ try {
+ oneParam = CustomLibClass.oneParameterReturn(z1);
+ } catch (Exception e) {
+ throw new RuntimeException("Test failed.");
+ }
+ System.out.println(oneParam.getId());
+ }
+
+ public static void twoParametersReturn() {
+ ZoneId z1 = ZoneId.of(ZONE_ID);
+ ZoneId z2 = ZoneId.of(ZONE_ID);
+ ZoneId twoParam;
+ try {
+ twoParam = CustomLibClass.twoParametersReturn(z1, z2);
+ } catch (Exception e) {
+ throw new RuntimeException("Test failed.");
+ }
+ System.out.println(twoParam.getId());
+ }
+
+ public static void oneParameter() {
+ ZoneId z1 = ZoneId.of(ZONE_ID);
+ String res;
+ try {
+ res = CustomLibClass.oneParameter(z1);
+ } catch (Exception e) {
+ throw new RuntimeException("Test failed.");
+ }
+ System.out.println(res);
+ }
+
+ public static void twoParameters() {
+ ZoneId z1 = ZoneId.of(ZONE_ID);
+ ZoneId z2 = ZoneId.of(ZONE_ID);
+ String res;
+ try {
+ res = CustomLibClass.twoParameters(z1, z2);
+ } catch (Exception e) {
+ throw new RuntimeException("Test failed.");
+ }
+ System.out.println(res);
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ public static void twoParametersThrow() {
+ ZoneId z1 = ZoneId.of(ZONE_ID);
+ ZoneId z2 = ZoneId.of(ZONE_ID);
+ try {
+ CustomLibClass.twoParametersThrow(z1, z2);
+ throw new RuntimeException("Test failed.");
+ } catch (ArithmeticException e) {
+ System.out.println("Exception caught");
+ }
+ }
+ }
+
+ // This class will be put at compilation time as library and on the runtime class path.
+ // This class is convenient for easy testing. Each method plays the role of methods in the
+ // platform APIs for which argument/return values need conversion.
+ @SuppressWarnings("WeakerAccess")
+ static class CustomLibClass {
+
+ private static final String ZONE_ID = "GMT";
+
+ public static ZoneId returnOnly() {
+ return ZoneId.of(ZONE_ID);
+ }
+
+ public static ZoneId oneParameterReturn(ZoneId z1) {
+ return z1;
+ }
+
+ public static ZoneId twoParametersReturn(ZoneId z1, ZoneId z2) {
+ return z1;
+ }
+
+ public static String oneParameter(ZoneId z1) {
+ return z1.getId();
+ }
+
+ public static String twoParameters(ZoneId z1, ZoneId z2) {
+ return z1.getId();
+ }
+
+ @SuppressWarnings({"divzero", "NumericOverflow", "UnusedReturnValue"})
+ public static String twoParametersThrow(ZoneId z1, ZoneId z2) {
+ return "" + (1 / 0);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11AtomicTests.java b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11AtomicTests.java
index 150fbf6..0010c65 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11AtomicTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11AtomicTests.java
@@ -19,6 +19,7 @@
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
+import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,6 +72,8 @@
@Test
public void testD8AtomicReference() throws Exception {
+ // TODO(b/142377475).
+ Assume.assumeTrue(!shrinkDesugaredLibrary);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String verbosity = "2";
testForD8()
@@ -94,6 +97,8 @@
@Test
public void testD8AtomicUpdaters() throws Exception {
+ // TODO(b/142377475).
+ Assume.assumeTrue(!shrinkDesugaredLibrary);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String verbosity = "2";
testForD8()
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ConcurrentMapTests.java b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ConcurrentMapTests.java
index d340245..f80607a 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ConcurrentMapTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ConcurrentMapTests.java
@@ -99,6 +99,8 @@
public void testD8Concurrent() throws Exception {
// TODO(b/134732760): Support Java 9+ libraries.
// We skip the ConcurrentRemoveIf test because of the non desugared class CompletableFuture.
+ // TODO(b/142377475).
+ Assume.assumeTrue(!shrinkDesugaredLibrary);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String verbosity = "2";
testForD8()
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11TimeTests.java b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11TimeTests.java
index d5debbb..51ce216 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11TimeTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11TimeTests.java
@@ -132,7 +132,9 @@
@Test
public void testTime() throws Exception {
// TODO(b/137876068): Temporarily ignored to move forward with Desugared API conversion.
- Assume.assumeFalse(parameters.getApiLevel().getLevel() <= AndroidApiLevel.M.getLevel());
+ // Extra conversions are required due to extra classes injected in the desugared library
+ // in this test. Disabled for now.
+ Assume.assumeFalse(parameters.getApiLevel().getLevel() <= AndroidApiLevel.N.getLevel());
String verbosity = "2";
D8TestCompileResult compileResult =
testForD8()
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
index 11179f5..fa917a3 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
@@ -1,6 +1,6 @@
{
"configuration_format_version": 1,
- "version": "0.3.0",
+ "version": "0.5.0",
"required_compilation_api_level": 26,
"library_flags": [
{
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringTrimTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringTrimTest.java
new file mode 100644
index 0000000..cd7618d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringTrimTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 StringTrimTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public StringTrimTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+
+ MethodSubject mainMethodSubject = classSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .anyMatch(x -> x.isConstString("Hello world!", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .noneMatch(x -> x.isConstString(" Hello world! ", JumboStringMode.ALLOW)));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(" Hello world! ".trim());
+ }
+ }
+}
diff --git a/third_party/iosched_2019.tar.gz.sha1 b/third_party/iosched_2019.tar.gz.sha1
new file mode 100644
index 0000000..257df06
--- /dev/null
+++ b/third_party/iosched_2019.tar.gz.sha1
@@ -0,0 +1 @@
+e1df1acda9f21519c802adbe3eef03c3e60bf8b8
\ No newline at end of file
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py
index 3ca25a6..903f3b8 100755
--- a/tools/archive_desugar_jdk_libs.py
+++ b/tools/archive_desugar_jdk_libs.py
@@ -19,6 +19,7 @@
# repository to fetch the artifact com.android.tools:desugar_jdk_libs:1.0.0
import archive
+import defines
import git_utils
import optparse
import os
@@ -27,6 +28,19 @@
import subprocess
import sys
import utils
+import zipfile
+
+if defines.IsLinux():
+ JDK8_JAVAC = os.path.join(
+ defines.THIRD_PARTY, 'openjdk', 'jdk8', 'linux-x86', 'bin', 'javac')
+elif defines.IsOsX():
+ JDK8_JAVAC = os.path.join(
+ defines.THIRD_PARTY, 'openjdk', 'jdk8', 'darwin-x86', 'bin', 'javac')
+elif defines.IsWindows():
+ raise Exception('Cannot compile using JDK8 on Windows hence cannot archive.')
+
+CONVERSION_FOLDER = os.path.join(
+ defines.REPO_ROOT, 'src', 'test', 'desugaredLibraryConversions')
VERSION_FILE = 'VERSION.txt'
LIBRARY_NAME = 'desugar_jdk_libs'
@@ -55,6 +69,10 @@
raise Exception('Version file '
+ VERSION_FILE + ' is expected to have exactly one line')
version = lines[0].strip()
+ if (version == '1.0.1'):
+ raise Exception('Version file ' + VERSION_FILE + 'cannot have version 1.0.1')
+ if (version == '1.0.0'):
+ version = '1.0.1'
utils.check_basic_semver_version(
version, 'in version file ' + VERSION_FILE)
return version
@@ -76,6 +94,13 @@
print('File available at: %s' %
destination.replace('gs://', 'http://storage.googleapis.com/', 1))
+def GetFilesInFolder(folder):
+ resulting_files = []
+ for root, dirs, files in os.walk(folder):
+ for name in files:
+ resulting_files.append(os.path.join(root, name))
+ assert len(resulting_files) > 0
+ return resulting_files
def Main(argv):
(options, args) = ParseOptions(argv)
@@ -95,6 +120,7 @@
# Make sure bazel is extracted in third_party.
utils.DownloadFromGoogleCloudStorage(utils.BAZEL_SHA_FILE)
+ utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
# Only handling versioned desugar_jdk_libs.
is_master = False
@@ -113,19 +139,53 @@
'Target archive directory %s already exists' % destination)
bazel = os.path.join(utils.BAZEL_TOOL, 'lib', 'bazel', 'bin', 'bazel')
- cmd = [bazel, 'build', 'maven_release']
+ cmd = [bazel, 'build', '--host_force_python=PY2', 'maven_release']
utils.PrintCmd(cmd)
subprocess.check_call(cmd)
cmd = [bazel, 'shutdown']
utils.PrintCmd(cmd)
subprocess.check_call(cmd)
+ # Compile the stubs for conversion files compilation.
+ stub_compiled_folder = os.path.join(checkout_dir, 'stubs')
+ os.mkdir(stub_compiled_folder)
+ all_stubs = GetFilesInFolder(os.path.join(CONVERSION_FOLDER, 'stubs'))
+ cmd = [JDK8_JAVAC, '-d', stub_compiled_folder] + all_stubs
+ utils.PrintCmd(cmd)
+ subprocess.check_call(cmd)
+
+ # Compile the conversion files.
+ conversions_compiled_folder = os.path.join(checkout_dir, 'conversions')
+ os.mkdir(conversions_compiled_folder)
+ all_conversions = GetFilesInFolder(
+ os.path.join(CONVERSION_FOLDER, 'conversions'))
+ cmd = [JDK8_JAVAC, '-cp', stub_compiled_folder, '-d',
+ conversions_compiled_folder] + all_conversions
+ utils.PrintCmd(cmd)
+ subprocess.check_call(cmd)
+
# Locate the library jar and the maven zip with the jar from the
# bazel build.
library_jar = os.path.join(
'bazel-bin', 'src', 'share', 'classes', 'java', 'libjava.jar')
maven_zip = os.path.join('bazel-bin', LIBRARY_NAME +'.zip')
+ # Make a writable copy of the jar.
+ jar_folder = os.path.join(checkout_dir, 'jar')
+ os.mkdir(jar_folder)
+ shutil.copy(library_jar, jar_folder)
+ library_jar = os.path.join(jar_folder,'libjava.jar')
+ os.chmod(library_jar, 0o777)
+
+ # Add conversion classes into the jar.
+ all_compiled_conversions = GetFilesInFolder(conversions_compiled_folder)
+ with zipfile.ZipFile(library_jar, mode='a', allowZip64=True) as jar:
+ for clazz in all_compiled_conversions:
+ jar.write(clazz,arcname=os.path.relpath(
+ clazz,
+ conversions_compiled_folder),
+ compress_type = zipfile.ZIP_DEFLATED)
+
storage_path = LIBRARY_NAME + '/' + version
# Upload the jar file with the library.
destination = archive.GetUploadDestination(
diff --git a/tools/internal_test.py b/tools/internal_test.py
index a0cc52b..6c4b7ff 100755
--- a/tools/internal_test.py
+++ b/tools/internal_test.py
@@ -39,7 +39,6 @@
# How often the bot/tester should check state
PULL_DELAY = 30
-BUCKET = 'r8-test-results'
TEST_RESULT_DIR = 'internal'
# Magic files
@@ -55,13 +54,94 @@
EXITCODE = 'exitcode'
TIMED_OUT = 'timed_out'
+BENCHMARK_APPS = [
+ {
+ 'app': 'r8',
+ 'version': 'cf',
+ 'find-xmx-min': 128,
+ 'find-xmx-max': 400,
+ 'find-xmx-range': 16,
+ 'oom-threshold': 247,
+ },
+ {
+ 'app': 'chrome',
+ 'version': '180917',
+ 'find-xmx-min': 256,
+ 'find-xmx-max': 450,
+ 'find-xmx-range': 16,
+ 'oom-threshold': 392,
+ },
+ {
+ 'app': 'youtube',
+ 'version': '12.22',
+ 'find-xmx-min': 1200,
+ 'find-xmx-max': 800,
+ 'find-xmx-range': 32,
+ 'oom-threshold': 1000,
+ },
+ # TODO(b/142375244): Narrow when run a few times.
+ {
+ 'app': 'iosched',
+ 'version': '2019',
+ 'find-xmx-min': 128,
+ 'find-xmx-max': 1024,
+ 'find-xmx-range': 16,
+ 'oom-threshold': 666, # will be changed after b/142375244 has run
+ },
+]
+
+def find_min_xmx_command(record):
+ return [
+ 'tools/run_on_app.py',
+ '--compiler=r8',
+ '--compiler-build=lib',
+ '--app=%s' % record['app'],
+ '--version=%s' % record['version'],
+ '--no-debug',
+ '--no-build',
+ '--find-min-xmx',
+ '--find-min-xmx-min-memory=%s' % record['find-xmx-min'],
+ '--find-min-xmx-max-memory=%s' % record['find-xmx-max'],
+ '--find-min-xmx-range-size=%s' % record['find-xmx-range'],
+ '--find-min-xmx-archive']
+
+def compile_with_memory_max_command(record):
+ return [
+ 'tools/run_on_app.py',
+ '--compiler=r8',
+ '--compiler-build=lib',
+ '--app=%s' % record['app'],
+ '--version=%s' % record['version'],
+ '--no-debug',
+ '--no-build',
+ '--max-memory=%s' % int(record['oom-threshold'] * 1.1)
+ ]
+
+def compile_with_memory_min_command(record):
+ return [
+ 'tools/run_on_app.py',
+ '--compiler=r8',
+ '--compiler-build=lib',
+ '--app=%s' % record['app'],
+ '--version=%s' % record['version'],
+ '--no-debug',
+ '--no-build',
+ '--expect-oom',
+ '--max-memory=%s' % int(record['oom-threshold'] * 0.9)
+ ]
+
TEST_COMMANDS = [
# Run test.py internal testing.
['tools/test.py', '--only_internal', '--slow_tests',
'--java_max_memory_size=8G'],
# Ensure that all internal apps compile.
- ['tools/run_on_app.py', '--ignore-java-version','--run-all', '--out=out']
-]
+ ['tools/run_on_app.py', '--run-all', '--out=out'],
+ # Find min xmx for selected benchmark apps
+ ['tools/gradle.py', 'r8lib'],
+] + (map(compile_with_memory_max_command, BENCHMARK_APPS)
+ + map(compile_with_memory_min_command, BENCHMARK_APPS)
+ + map(find_min_xmx_command, BENCHMARK_APPS))
+
# Command timeout, in seconds.
RUN_TIMEOUT = 3600 * 6
@@ -123,30 +203,19 @@
return utils.get_HEAD_sha1()
def get_test_result_dir():
- return os.path.join(BUCKET, TEST_RESULT_DIR)
+ return os.path.join(utils.R8_TEST_RESULTS_BUCKET, TEST_RESULT_DIR)
def get_sha_destination(sha):
return os.path.join(get_test_result_dir(), sha)
def archive_status(failed):
gs_destination = 'gs://%s' % get_sha_destination(utils.get_HEAD_sha1())
- archive_value('status', gs_destination, failed)
+ utils.archive_value('status', gs_destination, failed)
def get_status(sha):
gs_destination = 'gs://%s/status' % get_sha_destination(sha)
return utils.cat_file_on_cloud_storage(gs_destination)
-def archive_file(name, gs_dir, src_file):
- gs_file = '%s/%s' % (gs_dir, name)
- utils.upload_file_to_cloud_storage(src_file, gs_file, public_read=False)
-
-def archive_value(name, gs_dir, value):
- with utils.TempDir() as temp:
- tempfile = os.path.join(temp, name);
- with open(tempfile, 'w') as f:
- f.write(str(value))
- archive_file(name, gs_dir, tempfile)
-
def archive_log(stdout, stderr, exitcode, timed_out, cmd):
sha = utils.get_HEAD_sha1()
cmd_dir = cmd.replace(' ', '_').replace('/', '_')
@@ -154,10 +223,10 @@
gs_destination = 'gs://%s' % destination
url = 'https://storage.cloud.google.com/%s' % destination
log('Archiving logs to: %s' % gs_destination)
- archive_value(EXITCODE, gs_destination, exitcode)
- archive_value(TIMED_OUT, gs_destination, timed_out)
- archive_file(STDOUT, gs_destination, stdout)
- archive_file(STDERR, gs_destination, stderr)
+ utils.archive_value(EXITCODE, gs_destination, exitcode)
+ utils.archive_value(TIMED_OUT, gs_destination, timed_out)
+ utils.archive_file(STDOUT, gs_destination, stdout)
+ utils.archive_file(STDERR, gs_destination, stderr)
log('Logs available at: %s' % url)
def get_magic_file_base_path():
@@ -173,7 +242,7 @@
utils.delete_file_from_cloud_storage(get_magic_file_gs_path(name))
def put_magic_file(name, sha):
- archive_value(name, get_magic_file_base_path(), sha)
+ utils.archive_value(name, get_magic_file_base_path(), sha)
def get_magic_file_content(name, ignore_errors=False):
return utils.cat_file_on_cloud_storage(get_magic_file_gs_path(name),
diff --git a/tools/iosched_data.py b/tools/iosched_data.py
new file mode 100644
index 0000000..10edd2b
--- /dev/null
+++ b/tools/iosched_data.py
@@ -0,0 +1,176 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import os
+import utils
+
+BASE = os.path.join(utils.THIRD_PARTY, 'iosched_2019')
+
+INPUT_JARS = [
+ '1258593f0b2b739993b682bbaf9fbb78.jar',
+ '89e9dbba1fbe6b0fb368f1e95589a721.jar',
+ '1fc8941eba5f85af1a8b7da23c729ec1.jar',
+ 'ea69d715e74c5a1a5796f035243f3e97.jar',
+ '811d2312b489a587d1581f4e37d637f7.jar',
+ 'core-common-2.1.0-beta01.jar',
+ 'annotations-4.6.1.jar',
+ 'okhttp-3.12.1.jar',
+ 'd55e0fc606c88c93df0d2c3ad9fbc66e.jar',
+ 'a88afc3d193dd0fa82c07cb40636e3ca.jar',
+ '9a226aecc46543548aa3b8d516848199.jar',
+ '57aa054ef470b0eef00e8fd0b0d634bc.jar',
+ '04db891d0460d81f9bf27f14c75ab1c1.jar',
+ 'okhttp-2.7.5.jar',
+ '66e02cfd6fc641b6806c903ab1385c8f.jar',
+ '429150960927f98f31cfb8f93acce495.jar',
+ 'gson-2.8.1.jar',
+ '9f174a718fccec6dd22216b50e2024dd.jar',
+ 'fccc983abc03ed47fb7f92e27c01f754.jar',
+ 'f682c9b637850b361f6987e36818df61.jar',
+ 'a2ff39bc73ca8ad09f4da1873bb0c86b.jar',
+ '3e62317d1e8cb1c33d978124952bbdde.jar',
+ '6d7cf1b5cfc5deefb39819e6927763d2.jar',
+ '684e8bb7e37e94adea3de977d6b8d785.jar',
+ '6da5a19a73b7295a9a14ebc739c15513.jar',
+ 'e8d3c32813b7956c025e0b94ef0f2d3f.jar',
+ 'annotations-4.1.1.4.jar',
+ '19c2c18938ee2a99115dd700b18692c2.jar',
+ '87ae4245c0a8fe0ba2dcd40275a203cf.jar',
+ 'databinding-common-3.4.1.jar',
+ '27556a10d0fdbd9730a712a196a1ea33.jar',
+ 'kotlinx-coroutines-core-1.1.1.jar',
+ 'bdb2bd187f4ec8a364ebc58e0682bf5c.jar',
+ '72054a54b7a1555355136f0f12550439.jar',
+ 'fd24a8a68ac1192744d79cb3f8a50bff.jar',
+ 'disklrucache-4.6.1.jar',
+ 'de1154a48df042b18af6f861738b1f4f.jar',
+ '49d0c995b51f741d1cd5095ff50aa610.jar',
+ '5a906c2026b29fcf8e38dfc091dedf5f.jar',
+ 'error_prone_annotations-2.3.2.jar',
+ 'fc3371678bf5b92c8ad09bff3cca7d34.jar',
+ 'b1328978c6137d4afd05e8b449089790.jar',
+ '5d9bc74a14654749fec1bd3f57868314.jar',
+ '4ca93957d039e88756ce172a3f23f92c.jar',
+ 'b3ae138529e3059714654ee61047c60c.jar',
+ '04c45dab1f83131034d1e8ff7a7e4062.jar',
+ '6d09c79fcadbf28f15bd5d302f1da434.jar',
+ '3c5f06144063c91423d28cbd925d9a93.jar',
+ 'room-common-2.1.0.jar',
+ 'cc16ff10efeb6d0fb62ab09980fa6368.jar',
+ '095c50fdef7bb1f16479501c204965c3.jar',
+ '0f8cbd6ea38249489aa4b6e08f637a9c.jar',
+ 'dfba629a83faeb465bc5281f59b33410.jar',
+ 'kotlin-android-extensions-runtime-1.3.41.jar',
+ '36e56f8acb47c47dc4aba993954571df.jar',
+ 'animal-sniffer-annotations-1.17.jar',
+ '50262b47ca79a3e0887f4363cee3269a.jar',
+ '653fd9ff51b91c8cb7e0b8b4933292e7.jar',
+ 'bd5cfb8566d0f18b7349220f77b99038.jar',
+ 'annotations-16.0.1.jar',
+ 'checker-compat-qual-2.5.2.jar',
+ 'fad1e20626a9c0638b081c19bc5b8a39.jar',
+ '82e00b90bc477d17e9da6a4c8ff9ecea.jar',
+ '67302edfd6d295aff1eb26c436372785.jar',
+ 'ce77a2a47382a2f825868572b889d2c7.jar',
+ 'javax.inject-1.jar',
+ '96f7d3ab17962219c936737346db0f85.jar',
+ 'f3393d6b2b44cbf7b1344884834e89b0.jar',
+ '54ffaad03867b0dadad722ce3fb9960b.jar',
+ 'grpc-api-1.21.0.jar',
+ '91ed4f2ea5824596c9b228ad297f23b3.jar',
+ '67a09a53395866b35f9cd93bd668c84e.jar',
+ 'e3d353a45188abc61dd104d69fa1dd82.jar',
+ 'jetified-kotlinx-coroutines-android-1.1.1.jar',
+ '5ecec3c73e22ced41f9a40a26c0e5281.jar',
+ '2771336e639c434ce717c9ac58875dc2.jar',
+ 'e9bfc218eb8cdc6f1110f0d048c4862d.jar',
+ '38ace74613d265c1f1cccbb1fa10f627.jar',
+ '562cc9d1cf74343dfb5fd7ced76ba2c4.jar',
+ 'opencensus-api-0.21.0.jar',
+ 'd337a2d970073e4d08c0afa5ad287a45.jar',
+ '50ed6ac2559a57159d48c24be1ec847a.jar',
+ 'a3e463112f8c2d5d0e99f08c274e4df7.jar',
+ 'ae7980f49ccbaa525389f6b7e73240fe.jar',
+ '8e0a68dd4e7fa4c84c2471cc6b46ad98.jar',
+ '7072528e00796ef6011ab617dfdbed57.jar',
+ '45c6fe8ab0e41a359c77e925bfe8a467.jar',
+ 'room-common-java8-2.1.0.jar',
+ 'fc5e4f8937eeeac973e266d99abdcbc9.jar',
+ 'lifecycle-common-2.1.0-beta01.jar',
+ 'guava-26.0-android.jar',
+ 'grpc-stub-1.21.0.jar',
+ 'jsr305-3.0.2.jar',
+ '02941d2c97d179cc4a4187fbf269010b.jar',
+ 'grpc-okhttp-1.21.0.jar',
+ '79b02b1828fc13f97dfd7c60656f58a3.jar',
+ '97123a1bfa3e489812597dd9ca6a476f.jar',
+ 'c87d55c5aff3cfb40573462b40c16e93.jar',
+ 'd5d50e3f4be084f67aea3d7274996dac.jar',
+ '22798eb6762e1b6e1ed68e0a26a11416.jar',
+ 'opencensus-contrib-grpc-metrics-0.21.0.jar',
+ 'e7d37c099e227a9a8063906d2d2fac39.jar',
+ '58fa238dea0a0425db38a9ef7614544a.jar',
+ '656d56b89a5bdfdb34c6df1a03cdb44a.jar',
+ 'cd4453bc1349dc354726f443d14e42a1.jar',
+ 'model.jar',
+ '9b1a33316cb5646c0c7f2409b59a6e3e.jar',
+ 'ab21f6ad1b9d44aef2e4f748aadbd6e0.jar',
+ 'd84ac18db47f5c63a7c906981a8c74c2.jar',
+ 'auto-value-annotations-1.6.3.jar',
+ 'e14a0047c43e74969599da9eecd2c326.jar',
+ 'f12c26a7262a280545381f23a1e99fae.jar',
+ 'd69110fa9aef88987146fb1855fc797b.jar',
+ '147569cf828c2f744bbbc06c0f207c31.jar',
+ 'grpc-protobuf-lite-1.21.0.jar',
+ 'da0774b870df803349a111b937795a71.jar',
+ '2624862b1ed2676a15e6c4ecacdb2faa.jar',
+ 'grpc-context-1.21.0.jar',
+ 'logging-interceptor-3.10.0.jar',
+ 'threetenbp-1.3.6-no-tzdb.jar',
+ '5b200d64a4850d02b6965e2bce869691.jar',
+ '55ad080882c073b8ccc51a9c516ec407.jar',
+ '1e61604b06dc10dcdd1cb9edab5f3411.jar',
+ '6d74ce3d9ef226c95dd9e0589df9d8c0.jar',
+ '1fdcde2b38692788ec8efcab3958b42c.jar',
+ '50bef79f89b168cefcfbcad72f8cabfa.jar',
+ 'annotation-1.1.0.jar',
+ '956e5331c88f28df168ce3ea2402dffc.jar',
+ '471bc8aa5928f4fbb3c9f00cc6f907ca.jar',
+ 'dagger-2.24.jar',
+ '234bdadd74c099896158187676ea0650.jar',
+ 'kotlin-stdlib-1.3.41.jar',
+ 'j2objc-annotations-1.1.jar',
+ 'fe2d5d6bd2084907c7dd971f366042e6.jar',
+ '66fb157c1fdab977f2e15c1dcc222649.jar',
+ '18ded358602e992e34f5131e231c9fdd.jar',
+ 'cb7e7d424e289578c115667747b262cd.jar',
+ '0ed7d992c263060a704a951b53ac0bbb.jar',
+ '6a495a5466b08b8e825e78918680b946.jar',
+ '7c3656acf184f5103c65f49acad46981.jar',
+ 'classes_0.jar',
+ '47bbfe33b04bd140773a33c708d5df34.jar',
+ 'grpc-core-1.21.0.jar',
+ 'collection-1.1.0.jar',
+ 'programclasses.jar',
+ '592c92d42c935f46f0e50e988310a94b.jar',
+ 'constraintlayout-solver-1.1.3.jar',
+ 'protobuf-lite-3.0.1.jar',
+ '309d47704a456783a746357a53f76ed9.jar',
+ 'kotlin-stdlib-jdk7-1.3.41.jar',
+ 'e47b3993e1f3cb81b6a13a44a60edc7d.jar',
+ 'classes.jar',
+ 'e9385c983e864be1243e1865c529f2ba.jar',
+ 'okio-1.15.0.jar',
+ '492c0eca59ede4f2e0dd149bb44e0c5a.jar',
+]
+
+VERSIONS = {
+ '2019': {
+ 'deploy' : {
+ 'inputs': [os.path.join(BASE, path) for path in INPUT_JARS],
+ 'pgconf': [os.path.join(BASE, 'proguard-rules.pro')],
+ 'libraries': [utils.get_android_jar(28)],
+ },
+ },
+}
diff --git a/tools/r8_data.py b/tools/r8_data.py
index 71b1f12..c4c509f 100644
--- a/tools/r8_data.py
+++ b/tools/r8_data.py
@@ -5,6 +5,8 @@
import utils
import os
+ANDROID_L_API = '21'
+
VERSIONS = {
'cf': {
'deploy': {
@@ -12,6 +14,11 @@
'pgconf': [os.path.join(utils.REPO_ROOT, 'src', 'main', 'keep.txt')],
'libraries' : [utils.RT_JAR],
'flags': '--classfile',
+ },
+ 'proguarded': {
+ 'inputs': [utils.PINNED_R8_JAR],
+ 'libraries' : [utils.RT_JAR],
+ 'min-api' : ANDROID_L_API,
}
}
}
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index f2592c9..b32581b 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -21,9 +21,10 @@
import youtube_data
import chrome_data
import r8_data
+import iosched_data
TYPES = ['dex', 'deploy', 'proguarded']
-APPS = ['gmscore', 'nest', 'youtube', 'gmail', 'chrome', 'r8']
+APPS = ['gmscore', 'nest', 'youtube', 'gmail', 'chrome', 'r8', 'iosched']
COMPILERS = ['d8', 'r8']
COMPILER_BUILDS = ['full', 'lib']
@@ -33,6 +34,10 @@
# A negative value -N indicates that the child was terminated by signal N.
TIMEOUT_KILL_CODE = -9
+# Log file names
+FIND_MIN_XMX_FILE = 'find_min_xmx_results'
+FIND_MIN_XMX_DIR = 'find_min_xmx'
+
def ParseOptions(argv):
result = optparse.OptionParser()
result.add_option('--compiler',
@@ -49,6 +54,10 @@
help='Compile all possible combinations',
default=False,
action='store_true')
+ result.add_option('--expect-oom',
+ help='Expect that compilation will fail with an OOM',
+ default=False,
+ action='store_true')
result.add_option('--type',
help='Default for R8: deploy, for D8: proguarded',
choices=TYPES)
@@ -59,6 +68,9 @@
help='Run without building first',
default=False,
action='store_true')
+ result.add_option('--max-memory',
+ help='The maximum memory in MB to run with',
+ type='int')
result.add_option('--find-min-xmx',
help='Find the minimum amount of memory we can run in',
default=False,
@@ -73,8 +85,12 @@
help='Setting the size of the acceptable memory range',
type='int',
default=32)
+ result.add_option('--find-min-xmx-archive',
+ help='Archive find-min-xmx results on GCS',
+ default=False,
+ action='store_true')
result.add_option('--timeout',
- type=int,
+ type='int',
default=0,
help='Set timeout instead of waiting for OOM.')
result.add_option('--golem',
@@ -132,6 +148,7 @@
metavar='BENCHMARKNAME',
help='Print the sizes of individual dex segments as ' +
'\'<BENCHMARKNAME>-<segment>(CodeSize): <bytes>\'')
+
return result.parse_args(argv)
# Most apps have -printmapping, -printseeds, -printusage and
@@ -162,7 +179,8 @@
'youtube': youtube_data,
'chrome': chrome_data,
'gmail': gmail_data,
- 'r8': r8_data
+ 'r8': r8_data,
+ 'iosched': iosched_data,
}
# Check to ensure that we add all variants here.
assert len(APPS) == len(data_providers)
@@ -234,16 +252,73 @@
not_working = next_candidate
assert working - not_working <= range
- print('Found range: %s - %s' % (not_working, working))
+ found_range = 'Found range: %s - %s' % (not_working, working)
+ print(found_range)
+
+ if options.find_min_xmx_archive:
+ sha = utils.get_HEAD_sha1()
+ (version, _) = get_version_and_data(options)
+ destination = os.path.join(
+ utils.R8_TEST_RESULTS_BUCKET,
+ FIND_MIN_XMX_DIR,
+ sha,
+ options.compiler,
+ options.compiler_build,
+ options.app,
+ version,
+ get_type(options))
+ gs_destination = 'gs://%s' % destination
+ utils.archive_value(FIND_MIN_XMX_FILE, gs_destination, found_range + '\n')
+
return 0
def main(argv):
(options, args) = ParseOptions(argv)
+ if options.expect_oom and not options.max_memory:
+ raise Exception(
+ 'You should only use --expect-oom if also specifying --max-memory')
+ if options.expect_oom and options.timeout:
+ raise Exception(
+ 'You should not use --timeout when also specifying --expect-oom')
if options.run_all:
return run_all(options, args)
if options.find_min_xmx:
return find_min_xmx(options, args)
- return run_with_options(options, args)
+ exit_code = run_with_options(options, args)
+ if options.expect_oom:
+ exit_code = 0 if exit_code == OOM_EXIT_CODE else 1
+ return exit_code
+
+def get_version_and_data(options):
+ if options.app == 'gmscore':
+ version = options.version or 'v9'
+ data = gmscore_data
+ elif options.app == 'nest':
+ version = options.version or '20180926'
+ data = nest_data
+ elif options.app == 'youtube':
+ version = options.version or '12.22'
+ data = youtube_data
+ elif options.app == 'chrome':
+ version = options.version or '180917'
+ data = chrome_data
+ elif options.app == 'gmail':
+ version = options.version or '170604.16'
+ data = gmail_data
+ elif options.app == 'r8':
+ version = options.version or 'cf'
+ data = r8_data
+ elif options.app == 'iosched':
+ version = options.version or '2019'
+ data = iosched_data
+ else:
+ raise Exception("You need to specify '--app={}'".format('|'.join(APPS)))
+ return version, data
+
+def get_type(options):
+ if not options.type:
+ return 'deploy' if options.compiler == 'r8' else 'proguarded'
+ return options.type
def run_with_options(options, args, extra_args=None):
if extra_args is None:
@@ -251,7 +326,10 @@
app_provided_pg_conf = False;
# todo(121018500): remove when memory is under control
if not any('-Xmx' in arg for arg in extra_args):
- extra_args.append('-Xmx8G')
+ if options.max_memory:
+ extra_args.append('-Xmx%sM' % options.max_memory)
+ else:
+ extra_args.append('-Xmx8G')
if options.golem:
golem.link_third_party()
options.out = os.getcwd()
@@ -259,27 +337,7 @@
utils.check_java_version()
outdir = options.out
- data = None
- if options.app == 'gmscore':
- options.version = options.version or 'v9'
- data = gmscore_data
- elif options.app == 'nest':
- options.version = options.version or '20180926'
- data = nest_data
- elif options.app == 'youtube':
- options.version = options.version or '12.22'
- data = youtube_data
- elif options.app == 'chrome':
- options.version = options.version or '180917'
- data = chrome_data
- elif options.app == 'gmail':
- options.version = options.version or '170604.16'
- data = gmail_data
- elif options.app == 'r8':
- options.version = options.version or 'cf'
- data = r8_data
- else:
- raise Exception("You need to specify '--app={}'".format('|'.join(APPS)))
+ (version_id, data) = get_version_and_data(options)
if options.compiler not in COMPILERS:
raise Exception("You need to specify '--compiler={}'"
@@ -289,32 +347,31 @@
raise Exception("You need to specify '--compiler-build={}'"
.format('|'.join(COMPILER_BUILDS)))
- if not options.version in data.VERSIONS.keys():
+ if not version_id in data.VERSIONS.keys():
print('No version {} for application {}'
- .format(options.version, options.app))
+ .format(version_id, options.app))
print('Valid versions are {}'.format(data.VERSIONS.keys()))
return 1
- version = data.VERSIONS[options.version]
+ version = data.VERSIONS[version_id]
- if not options.type:
- options.type = 'deploy' if options.compiler == 'r8' \
- else 'proguarded'
+ type = get_type(options)
- if options.type not in version:
- print('No type {} for version {}'.format(options.type, options.version))
+ if type not in version:
+ print('No type {} for version {}'.format(type, version))
print('Valid types are {}'.format(version.keys()))
return 1
- values = version[options.type]
+ values = version[type]
inputs = None
# For R8 'deploy' the JAR is located using the Proguard configuration
# -injars option. For chrome and nest we don't have the injars in the
# proguard files.
if 'inputs' in values and (options.compiler != 'r8'
- or options.type != 'deploy'
+ or type != 'deploy'
or options.app == 'chrome'
or options.app == 'nest'
- or options.app == 'r8'):
+ or options.app == 'r8'
+ or options.app == 'iosched'):
inputs = values['inputs']
args.extend(['--output', outdir])
diff --git a/tools/utils.py b/tools/utils.py
index 152c1e7..fb937e6 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -94,11 +94,25 @@
OPENSOURCE_APPS_FOLDER = os.path.join(THIRD_PARTY, 'opensource_apps')
BAZEL_SHA_FILE = os.path.join(THIRD_PARTY, 'bazel.tar.gz.sha1')
BAZEL_TOOL = os.path.join(THIRD_PARTY, 'bazel')
+JAVA8_SHA_FILE = os.path.join(THIRD_PARTY, 'openjdk', 'jdk8', 'linux-x86.tar.gz.sha1')
ANDROID_HOME_ENVIROMENT_NAME = "ANDROID_HOME"
ANDROID_TOOLS_VERSION_ENVIRONMENT_NAME = "ANDROID_TOOLS_VERSION"
USER_HOME = os.path.expanduser('~')
+R8_TEST_RESULTS_BUCKET = 'r8-test-results'
+
+def archive_file(name, gs_dir, src_file):
+ gs_file = '%s/%s' % (gs_dir, name)
+ upload_file_to_cloud_storage(src_file, gs_file, public_read=False)
+
+def archive_value(name, gs_dir, value):
+ with TempDir() as temp:
+ tempfile = os.path.join(temp, name);
+ with open(tempfile, 'w') as f:
+ f.write(str(value))
+ archive_file(name, gs_dir, tempfile)
+
def getAndroidHome():
return os.environ.get(
ANDROID_HOME_ENVIROMENT_NAME, os.path.join(USER_HOME, 'Android', 'Sdk'))