Merge commit '0a5f2ef14924206827b179c5215d5aeb0b2080ce' into dev-release
diff --git a/build.gradle b/build.gradle
index 46548e4..eac0f5c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -264,14 +264,23 @@
main11Implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
examplesTestNGRunnerCompile group: 'org.testng', name: 'testng', version: testngVersion
+
testCompile sourceSets.examples.output
testCompile "junit:junit:$junitVersion"
+ testCompile "com.google.guava:guava:$guavaVersion"
testCompile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
testCompile "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
testCompile group: 'org.smali', name: 'smali', version: smaliVersion
testCompile files('third_party/jasmin/jasmin-2.4.jar')
testCompile files('third_party/jdwp-tests/apache-harmony-jdwp-tests-host.jar')
testCompile files('third_party/ddmlib/ddmlib.jar')
+ testCompile group: 'org.ow2.asm', name: 'asm', version: asmVersion
+ testCompile group: 'org.ow2.asm', name: 'asm-commons', version: asmVersion
+ testCompile group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion
+ testCompile group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
+ testCompile group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
+ testCompile group: 'it.unimi.dsi', name: 'fastutil', version: fastutilVersion
+
jctfCommonCompile "junit:junit:$junitVersion"
jctfTestsCompile "junit:junit:$junitVersion"
jctfTestsCompile sourceSets.jctfCommon.output
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 3b3e48e..ad3e92b 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -168,8 +168,7 @@
private static AppView<AppInfo> readApp(
AndroidApp inputApp, InternalOptions options, ExecutorService executor, Timing timing)
throws IOException {
- PrefixRewritingMapper rewritePrefix =
- options.desugaredLibrarySpecification.getPrefixRewritingMapper();
+ PrefixRewritingMapper rewritePrefix = options.getPrefixRewritingMapper();
ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
LazyLoadedDexApplication app = applicationReader.read(executor);
AppInfo appInfo = AppInfo.createInitialAppInfo(app, applicationReader.readMainDexClasses(app));
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 1edb900..7d1c9bd 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -63,7 +63,7 @@
MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
- SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
+ SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
MainDexRootSet mainDexRootSet =
MainDexRootSet.builder(appView, subtypingInfo, options.mainDexKeepRules).build(executor);
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index c3bf91f..9ba6c7f 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -166,8 +166,7 @@
LazyLoadedDexApplication lazyApp =
new ApplicationReader(inputApp, options, timing).read(executor);
- PrefixRewritingMapper rewritePrefix =
- options.desugaredLibrarySpecification.getPrefixRewritingMapper();
+ PrefixRewritingMapper rewritePrefix = options.getPrefixRewritingMapper();
DexApplication app = new L8TreePruner(options).prune(lazyApp, rewritePrefix);
return AppView.createForL8(AppInfo.createInitialAppInfo(app), rewritePrefix);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9d43396..b96eaef 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -352,7 +352,7 @@
options.itemFactory, options.getMinApiLevel()));
}
}
- SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
+ SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
appView.setRootSet(
RootSet.builder(
appView,
@@ -583,7 +583,7 @@
EnqueuerFactory.createForFinalTreeShaking(
appView,
executorService,
- new SubtypingInfo(appView),
+ SubtypingInfo.create(appView),
keptGraphConsumer,
prunedTypes);
if (options.isClassMergingExtensionRequired(enqueuer.getMode())) {
@@ -893,7 +893,7 @@
// computing from the initially computed main dex root set.
MainDexInfo mainDexInfo =
EnqueuerFactory.createForInitialMainDexTracing(
- appView, executorService, new SubtypingInfo(appView))
+ appView, executorService, SubtypingInfo.create(appView))
.traceMainDex(executorService, timing);
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
}
@@ -915,7 +915,7 @@
Enqueuer enqueuer =
EnqueuerFactory.createForFinalMainDexTracing(
- appView, executorService, new SubtypingInfo(appView), mainDexKeptGraphConsumer);
+ appView, executorService, SubtypingInfo.create(appView), mainDexKeptGraphConsumer);
// Find classes which may have code executed before secondary dex files installation.
MainDexInfo mainDexInfo = enqueuer.traceMainDex(executorService, timing);
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
@@ -1061,7 +1061,7 @@
// If there is no kept-graph info, re-run the enqueueing to compute it.
if (whyAreYouKeepingConsumer == null) {
whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(null);
- SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
+ SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
if (forMainDex) {
enqueuer =
EnqueuerFactory.createForFinalMainDexTracing(
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
index 929a010..4d42276 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -117,6 +117,6 @@
InitClassLens initClassLens) {
// ..., →
// ..., value
- frameBuilder.push(clazz);
+ frameBuilder.push(factory.intType);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index c2316e4..14b0ded 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -160,7 +160,7 @@
private static <T extends AppInfo> PrefixRewritingMapper defaultPrefixRewritingMapper(T appInfo) {
InternalOptions options = appInfo.options();
- return options.desugaredLibrarySpecification.getPrefixRewritingMapper();
+ return options.getPrefixRewritingMapper();
}
public static <T extends AppInfo> AppView<T> createForD8(T appInfo) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index 8618c4e..d0324ee 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -120,7 +120,10 @@
}
@Override
- public DexField getRenamedFieldSignature(DexField originalField) {
+ public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
+ if (this == codeLens) {
+ return originalField;
+ }
return originalFieldSignatures.inverse().getOrDefault(originalField, originalField);
}
@@ -132,8 +135,9 @@
}
@Override
- public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
- return GraphLens.getIdentityLens().lookupPrototypeChangesForMethodDefinition(method);
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+ DexMethod method, GraphLens codeLens) {
+ return GraphLens.getIdentityLens().lookupPrototypeChangesForMethodDefinition(method, codeLens);
}
@Override
@@ -153,7 +157,7 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
if (extraOriginalMethodSignatures.containsKey(method)) {
return extraOriginalMethodSignatures.get(method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
index 685188e..b2ecd15 100644
--- a/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
+++ b/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
@@ -71,7 +71,7 @@
// Add subtypes to worklist.
for (DexType subtype : immediateSubtypesProvider.apply(clazz.getType())) {
- DexClass definition = appView.definitionFor(subtype);
+ DexClass definition = definitionSupplier.contextIndependentDefinitionFor(subtype);
if (definition != null) {
if (scope != Scope.ONLY_PROGRAM_CLASSES || definition.isProgramClass()) {
addDependentsToWorklist(definition);
diff --git a/src/main/java/com/android/tools/r8/graph/ClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/graph/ClassHierarchyTraversal.java
index 83b09c3..4b48516 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassHierarchyTraversal.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassHierarchyTraversal.java
@@ -41,7 +41,7 @@
}
}
- final AppView<? extends AppInfoWithClassHierarchy> appView;
+ final DexDefinitionSupplier definitionSupplier;
final Scope scope;
final Set<DexClass> visited = new HashSet<>();
@@ -49,8 +49,8 @@
boolean excludeInterfaces = false;
- ClassHierarchyTraversal(AppView<? extends AppInfoWithClassHierarchy> appView, Scope scope) {
- this.appView = appView;
+ ClassHierarchyTraversal(DexDefinitionSupplier definitionSupplier, Scope scope) {
+ this.definitionSupplier = definitionSupplier;
this.scope = scope;
}
@@ -61,8 +61,8 @@
return self();
}
- public void visit(Iterable<DexProgramClass> sources, Consumer<T> visitor) {
- Iterator<DexProgramClass> sourceIterator = sources.iterator();
+ public void visit(Iterable<? extends DexClass> sources, Consumer<T> visitor) {
+ Iterator<? extends DexClass> sourceIterator = sources.iterator();
// Visit the program classes in the order that is implemented by addDependentsToWorklist().
while (sourceIterator.hasNext() || !worklist.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 6cef0f6..8c6bbc8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -868,12 +868,20 @@
forEachImmediateInterface(fn);
}
+ public void forEachImmediateSupertype(BiConsumer<DexType, Boolean> fn) {
+ if (superType != null) {
+ fn.accept(superType, false);
+ }
+ forEachImmediateInterface(iface -> fn.accept(iface, true));
+ }
+
public boolean validInterfaceSignatures() {
return getClassSignature().superInterfaceSignatures().isEmpty()
|| interfaces.values.length == getClassSignature().superInterfaceSignatures.size();
}
- public void forEachImmediateInterface(BiConsumer<DexType, ClassTypeSignature> consumer) {
+ public void forEachImmediateInterfaceWithSignature(
+ BiConsumer<DexType, ClassTypeSignature> consumer) {
assert validInterfaceSignatures();
// If there is no generic signature information don't pass any type arguments.
@@ -896,11 +904,12 @@
}
}
- public void forEachImmediateSupertype(BiConsumer<DexType, ClassTypeSignature> consumer) {
+ public void forEachImmediateSupertypeWithSignature(
+ BiConsumer<DexType, ClassTypeSignature> consumer) {
if (superType != null) {
consumer.accept(superType, classSignature.superClassSignature);
}
- forEachImmediateInterface(consumer);
+ forEachImmediateInterfaceWithSignature(consumer);
}
public void forEachImmediateInterfaceWithAppliedTypeArguments(
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 3ae4b1f..1aa41dc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -367,10 +367,33 @@
isInlinableByJavaC = true;
}
- public boolean isInlinableByJavaC() {
+ public boolean getIsInlinableByJavaC() {
return isInlinableByJavaC;
}
+ public boolean getOrComputeIsInlinableByJavaC(DexItemFactory dexItemFactory) {
+ if (getIsInlinableByJavaC()) {
+ return true;
+ }
+ if (!isStatic() || !isFinal()) {
+ return false;
+ }
+ if (!hasExplicitStaticValue()) {
+ return false;
+ }
+ if (getType().isPrimitiveType()) {
+ return true;
+ }
+ if (getType() != dexItemFactory.stringType) {
+ return false;
+ }
+ if (!getStaticValue().isDexValueString()) {
+ return false;
+ }
+ markAsInlinableByJavaC();
+ return true;
+ }
+
public static class Builder {
private DexField field;
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 4c76629..b27f94a 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -321,14 +321,18 @@
DexMethod original = method;
while (current.isNonIdentityLens() && current != atGraphLens) {
NonIdentityGraphLens nonIdentityLens = current.asNonIdentityLens();
- original = nonIdentityLens.internalGetPreviousMethodSignature(original);
+ original = nonIdentityLens.getPreviousMethodSignature(original);
current = nonIdentityLens.getPrevious();
}
assert atGraphLens == null ? current.isIdentityLens() : (current == atGraphLens);
return original;
}
- public abstract DexField getRenamedFieldSignature(DexField originalField);
+ public final DexField getRenamedFieldSignature(DexField originalField) {
+ return getRenamedFieldSignature(originalField, null);
+ }
+
+ public abstract DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens);
public final DexMember<?, ?> getRenamedMemberSignature(DexMember<?, ?> originalMember) {
return originalMember.isDexField()
@@ -451,8 +455,13 @@
MethodLookupResult lookupMethod(MethodLookupResult previous);
}
+ public final RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+ DexMethod method) {
+ return lookupPrototypeChangesForMethodDefinition(method, null);
+ }
+
public abstract RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
- DexMethod method);
+ DexMethod method, GraphLens codeLens);
public final DexField lookupField(DexField field) {
return lookupField(field, null);
@@ -485,14 +494,6 @@
FieldLookupResult lookupField(FieldLookupResult previous);
}
- public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
- return null;
- }
-
- public DexMethod lookupPutFieldForMethod(DexField field, DexMethod context) {
- return null;
- }
-
public DexReference lookupReference(DexReference reference) {
return reference.apply(this::lookupType, this::lookupField, this::lookupMethod);
}
@@ -521,6 +522,10 @@
return true;
}
+ public boolean hasCustomCodeRewritings() {
+ return false;
+ }
+
public boolean isAppliedLens() {
return false;
}
@@ -529,6 +534,10 @@
return false;
}
+ public boolean isEnumUnboxerLens() {
+ return false;
+ }
+
public abstract boolean isIdentityLens();
public boolean isMemberRebindingLens() {
@@ -553,6 +562,10 @@
return null;
}
+ public boolean isVerticalClassMergerLens() {
+ return false;
+ }
+
public GraphLens withCodeRewritingsApplied(DexItemFactory dexItemFactory) {
if (hasCodeRewritings()) {
return new ClearCodeRewritingGraphLens(dexItemFactory, this);
@@ -600,7 +613,7 @@
public Map<DexCallSite, ProgramMethodSet> rewriteCallSites(
Map<DexCallSite, ProgramMethodSet> callSites, DexDefinitionSupplier definitions) {
Map<DexCallSite, ProgramMethodSet> result = new IdentityHashMap<>();
- LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(definitions, this);
+ LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(definitions, this, null);
callSites.forEach(
(callSite, contexts) -> {
for (ProgramMethod context : contexts.rewrittenWithLens(definitions, this)) {
@@ -615,9 +628,16 @@
@SuppressWarnings("unchecked")
public <T extends DexReference> T rewriteReference(T reference) {
+ return rewriteReference(reference, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends DexReference> T rewriteReference(T reference, GraphLens codeLens) {
return (T)
reference.apply(
- this::lookupType, this::getRenamedFieldSignature, this::getRenamedMethodSignature);
+ type -> lookupType(type, codeLens),
+ field -> getRenamedFieldSignature(field, codeLens),
+ method -> getRenamedMethodSignature(method, codeLens));
}
public <T extends DexReference> Set<T> rewriteReferences(Set<T> references) {
@@ -767,9 +787,9 @@
}
@SuppressWarnings("unchecked")
- public final <T extends NonIdentityGraphLens> T findPrevious(
+ public final <T extends NonIdentityGraphLens> T find(
Predicate<NonIdentityGraphLens> predicate) {
- GraphLens current = getPrevious();
+ GraphLens current = this;
while (current.isNonIdentityLens()) {
NonIdentityGraphLens nonIdentityGraphLens = current.asNonIdentityLens();
if (predicate.test(nonIdentityGraphLens)) {
@@ -780,6 +800,13 @@
return null;
}
+ @SuppressWarnings("unchecked")
+ public final <T extends NonIdentityGraphLens> T findPrevious(
+ Predicate<NonIdentityGraphLens> predicate) {
+ GraphLens previous = getPrevious();
+ return previous.isNonIdentityLens() ? previous.asNonIdentityLens().find(predicate) : null;
+ }
+
public final void withAlternativeParentLens(GraphLens lens, Action action) {
GraphLens oldParent = getPrevious();
previousLens = lens;
@@ -864,7 +891,7 @@
}
return previousLens.internalLookupMethod(
reference,
- internalGetPreviousMethodSignature(context),
+ getPreviousMethodSignature(context),
type,
codeLens,
previous -> continuation.lookupMethod(internalDescribeLookupMethod(previous, context)));
@@ -877,7 +904,7 @@
protected abstract DexType internalDescribeLookupClassType(DexType previous);
- protected abstract DexMethod internalGetPreviousMethodSignature(DexMethod method);
+ public abstract DexMethod getPreviousMethodSignature(DexMethod method);
@Override
public final boolean isIdentityLens() {
@@ -931,7 +958,7 @@
}
@Override
- public DexField getRenamedFieldSignature(DexField originalField) {
+ public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
return originalField;
}
@@ -965,7 +992,7 @@
@Override
public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
- DexMethod method) {
+ DexMethod method, GraphLens codeLens) {
return RewrittenPrototypeDescription.none();
}
@@ -1025,8 +1052,10 @@
}
@Override
- public DexField getRenamedFieldSignature(DexField originalField) {
- return getPrevious().getRenamedFieldSignature(originalField);
+ public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
+ return this != codeLens
+ ? getPrevious().getRenamedFieldSignature(originalField)
+ : originalField;
}
@Override
@@ -1043,8 +1072,8 @@
@Override
public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
- DexMethod method) {
- return getIdentityLens().lookupPrototypeChangesForMethodDefinition(method);
+ DexMethod method, GraphLens codeLens) {
+ return getIdentityLens().lookupPrototypeChangesForMethodDefinition(method, codeLens);
}
@Override
@@ -1083,7 +1112,7 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
return method;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
index cec5083..521d6c7 100644
--- a/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
@@ -99,11 +99,6 @@
});
}
- public void forEachImmediateSubClass(
- DexProgramClass clazz, Consumer<? super DexProgramClass> consumer) {
- forEachImmediateSubClassMatching(clazz, alwaysTrue(), consumer);
- }
-
public void forEachImmediateSubClassMatching(
DexProgramClass clazz,
Predicate<? super DexProgramClass> predicate,
diff --git a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
index e5b3462..6ab93ed 100644
--- a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
@@ -130,7 +130,10 @@
}
@Override
- public DexField getRenamedFieldSignature(DexField originalField) {
+ public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
+ if (this == codeLens) {
+ return originalField;
+ }
DexField renamedField = getPrevious().getRenamedFieldSignature(originalField);
return internalGetNextFieldSignature(renamedField);
}
@@ -228,10 +231,14 @@
}
@Override
- public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
- DexMethod previous = internalGetPreviousMethodSignature(method);
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+ DexMethod method, GraphLens codeLens) {
+ if (this == codeLens) {
+ return getIdentityLens().lookupPrototypeChangesForMethodDefinition(method, codeLens);
+ }
+ DexMethod previous = getPreviousMethodSignature(method);
RewrittenPrototypeDescription lookup =
- getPrevious().lookupPrototypeChangesForMethodDefinition(previous);
+ getPrevious().lookupPrototypeChangesForMethodDefinition(previous, codeLens);
return internalDescribePrototypeChanges(lookup, method);
}
@@ -245,7 +252,7 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
return newMethodSignatures.getRepresentativeKeyOrDefault(method, method);
}
@@ -253,16 +260,6 @@
return newMethodSignatures.getRepresentativeValueOrDefault(method, method);
}
- @Override
- public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
- return getPrevious().lookupGetFieldForMethod(field, context);
- }
-
- @Override
- public DexMethod lookupPutFieldForMethod(DexField field, DexMethod context) {
- return getPrevious().lookupPutFieldForMethod(field, context);
- }
-
/**
* Default invocation type mapping.
*
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index bf3849c..972c83a 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -31,6 +31,7 @@
import it.unimi.dsi.fastutil.ints.IntLists;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -51,8 +52,13 @@
}
@Override
+ public boolean isNone() {
+ return true;
+ }
+
+ @Override
public ArgumentInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
return this;
}
@@ -80,6 +86,10 @@
return arg1.combine(arg2);
}
+ public boolean isNone() {
+ return false;
+ }
+
public boolean isRemovedArgumentInfo() {
return false;
}
@@ -100,7 +110,7 @@
public abstract ArgumentInfo combine(ArgumentInfo info);
public abstract ArgumentInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens graphLens);
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens);
@Override
public abstract boolean equals(Object obj);
@@ -113,9 +123,15 @@
public static class Builder {
+ private boolean checkNullOrZero;
private SingleValue singleValue;
private DexType type;
+ public Builder setCheckNullOrZero(boolean checkNullOrZero) {
+ this.checkNullOrZero = checkNullOrZero;
+ return this;
+ }
+
public Builder setSingleValue(SingleValue singleValue) {
this.singleValue = singleValue;
return this;
@@ -128,14 +144,16 @@
public RemovedArgumentInfo build() {
assert type != null;
- return new RemovedArgumentInfo(singleValue, type);
+ return new RemovedArgumentInfo(checkNullOrZero, singleValue, type);
}
}
+ private final boolean checkNullOrZero;
private final SingleValue singleValue;
private final DexType type;
- private RemovedArgumentInfo(SingleValue singleValue, DexType type) {
+ private RemovedArgumentInfo(boolean checkNullOrZero, SingleValue singleValue, DexType type) {
+ this.checkNullOrZero = checkNullOrZero;
this.singleValue = singleValue;
this.type = type;
}
@@ -156,8 +174,8 @@
return type;
}
- public boolean isNeverUsed() {
- return !hasSingleValue();
+ public boolean isCheckNullOrZeroSet() {
+ return checkNullOrZero;
}
@Override
@@ -178,12 +196,12 @@
@Override
public RemovedArgumentInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
SingleValue rewrittenSingleValue =
- hasSingleValue() ? singleValue.rewrittenWithLens(appView, graphLens) : null;
- DexType rewrittenType = graphLens.lookupType(type);
+ hasSingleValue() ? singleValue.rewrittenWithLens(appView, graphLens, codeLens) : null;
+ DexType rewrittenType = graphLens.lookupType(type, codeLens);
if (rewrittenSingleValue != singleValue || rewrittenType != type) {
- return new RemovedArgumentInfo(rewrittenSingleValue, rewrittenType);
+ return new RemovedArgumentInfo(checkNullOrZero, rewrittenSingleValue, rewrittenType);
}
return this;
}
@@ -194,12 +212,14 @@
return false;
}
RemovedArgumentInfo other = (RemovedArgumentInfo) obj;
- return type == other.type && Objects.equals(singleValue, other.singleValue);
+ return checkNullOrZero == other.checkNullOrZero
+ && type == other.type
+ && Objects.equals(singleValue, other.singleValue);
}
@Override
public int hashCode() {
- return Objects.hash(singleValue, type);
+ return Objects.hash(checkNullOrZero, singleValue, type);
}
}
@@ -214,12 +234,6 @@
return new Builder();
}
- public static RewrittenTypeInfo toVoid(
- DexType oldReturnType, DexItemFactory dexItemFactory, SingleValue singleValue) {
- assert singleValue != null;
- return new RewrittenTypeInfo(oldReturnType, dexItemFactory.voidType, null, singleValue);
- }
-
public RewrittenTypeInfo(DexType oldType, DexType newType) {
this(oldType, newType, null, null);
}
@@ -293,11 +307,14 @@
@Override
public RewrittenTypeInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
- DexType rewrittenCastType = castType != null ? graphLens.lookupType(castType) : null;
- DexType rewrittenNewType = graphLens.lookupType(newType);
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
+ DexType rewrittenCastType =
+ castType != null ? graphLens.lookupType(castType, codeLens) : null;
+ DexType rewrittenNewType = graphLens.lookupType(newType, codeLens);
SingleValue rewrittenSingleValue =
- hasSingleValue() ? getSingleValue().rewrittenWithLens(appView, graphLens) : null;
+ hasSingleValue()
+ ? getSingleValue().rewrittenWithLens(appView, graphLens, codeLens)
+ : null;
if (rewrittenCastType != castType
|| rewrittenNewType != newType
|| rewrittenSingleValue != singleValue) {
@@ -324,12 +341,6 @@
return Objects.hash(oldType, newType, singleValue);
}
- public boolean verifyIsDueToUnboxing(DexItemFactory dexItemFactory) {
- assert oldType.toBaseType(dexItemFactory).isClassType();
- assert newType.toBaseType(dexItemFactory).isIntType();
- return true;
- }
-
public static class Builder {
private DexType castType;
@@ -451,9 +462,9 @@
return removed;
}
- public int numberOfRemovedNonReceiverArguments(DexEncodedMethod method) {
+ public int numberOfRemovedNonReceiverArguments(ProgramMethod method) {
return numberOfRemovedArguments()
- - BooleanUtils.intValue(method.isInstance() && isArgumentRemoved(0));
+ - BooleanUtils.intValue(method.getDefinition().isInstance() && isArgumentRemoved(0));
}
public boolean hasArgumentInfo(int argumentIndex) {
@@ -469,11 +480,12 @@
}
public ArgumentInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
Int2ObjectSortedMap<ArgumentInfo> rewrittenArgumentInfos = new Int2ObjectRBTreeMap<>();
for (Int2ObjectMap.Entry<ArgumentInfo> entry : argumentInfos.int2ObjectEntrySet()) {
ArgumentInfo argumentInfo = entry.getValue();
- ArgumentInfo rewrittenArgumentInfo = argumentInfo.rewrittenWithLens(appView, graphLens);
+ ArgumentInfo rewrittenArgumentInfo =
+ argumentInfo.rewrittenWithLens(appView, graphLens, codeLens);
if (rewrittenArgumentInfo != argumentInfo) {
rewrittenArgumentInfos.put(entry.getIntKey(), rewrittenArgumentInfo);
}
@@ -529,46 +541,6 @@
}
}
- public DexMethod rewriteMethod(ProgramMethod method, DexItemFactory dexItemFactory) {
- if (isEmpty()) {
- return method.getReference();
- }
- DexProto rewrittenProto = rewriteProto(method, dexItemFactory);
- return method.getReference().withProto(rewrittenProto, dexItemFactory);
- }
-
- public DexProto rewriteProto(ProgramMethod method, DexItemFactory dexItemFactory) {
- return isEmpty()
- ? method.getProto()
- : dexItemFactory.createProto(method.getReturnType(), rewriteParameters(method));
- }
-
- public DexType[] rewriteParameters(ProgramMethod method) {
- return rewriteParameters(method.getDefinition());
- }
-
- public DexType[] rewriteParameters(DexEncodedMethod encodedMethod) {
- DexType[] params = encodedMethod.getParameters().values;
- if (isEmpty()) {
- return params;
- }
- DexType[] newParams =
- new DexType[params.length - numberOfRemovedNonReceiverArguments(encodedMethod)];
- int offset = encodedMethod.getFirstNonReceiverArgumentIndex();
- int newParamIndex = 0;
- for (int oldParamIndex = 0; oldParamIndex < params.length; oldParamIndex++) {
- ArgumentInfo argInfo = argumentInfos.get(oldParamIndex + offset);
- if (argInfo == null) {
- newParams[newParamIndex++] = params[oldParamIndex];
- } else if (argInfo.isRewrittenTypeInfo()) {
- RewrittenTypeInfo rewrittenTypeInfo = argInfo.asRewrittenTypeInfo();
- assert params[oldParamIndex] == rewrittenTypeInfo.oldType;
- newParams[newParamIndex++] = rewrittenTypeInfo.newType;
- }
- }
- return newParams;
- }
-
public ArgumentInfoCollection combine(ArgumentInfoCollection info) {
if (isEmpty()) {
return info;
@@ -782,44 +754,80 @@
public Instruction getConstantReturn(
AppView<AppInfoWithLiveness> appView,
IRCode code,
- ProgramMethod method,
Position position,
TypeAndLocalInfoSupplier info) {
assert rewrittenReturnInfo != null;
assert rewrittenReturnInfo.hasSingleValue();
- assert rewrittenReturnInfo.getSingleValue().isMaterializableInContext(appView, method);
Instruction instruction =
rewrittenReturnInfo.getSingleValue().createMaterializingInstruction(appView, code, info);
instruction.setPosition(position);
return instruction;
}
+ public boolean verifyConstantReturnAccessibleInContext(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod method, GraphLens codeLens) {
+ SingleValue rewrittenSingleValue =
+ rewrittenReturnInfo
+ .getSingleValue()
+ .rewrittenWithLens(appView, appView.graphLens(), codeLens);
+ assert rewrittenSingleValue.isMaterializableInContext(appView, method);
+ return true;
+ }
+
public DexMethod rewriteMethod(ProgramMethod method, DexItemFactory dexItemFactory) {
if (isEmpty()) {
return method.getReference();
}
- DexProto rewrittenProto = rewriteProto(method.getDefinition(), dexItemFactory);
+ DexProto rewrittenProto = rewriteProto(method, dexItemFactory);
return method.getReference().withProto(rewrittenProto, dexItemFactory);
}
- public DexProto rewriteProto(DexEncodedMethod encodedMethod, DexItemFactory dexItemFactory) {
+ public DexProto rewriteProto(ProgramMethod method, DexItemFactory dexItemFactory) {
if (isEmpty()) {
- return encodedMethod.getReference().proto;
+ return method.getProto();
}
DexType newReturnType =
- rewrittenReturnInfo != null
- ? rewrittenReturnInfo.newType
- : encodedMethod.getReference().proto.returnType;
- DexType[] newParameters = argumentInfoCollection.rewriteParameters(encodedMethod);
+ rewrittenReturnInfo != null ? rewrittenReturnInfo.getNewType() : method.getReturnType();
+ DexType[] newParameters = rewriteParameters(method, dexItemFactory);
return dexItemFactory.createProto(newReturnType, newParameters);
}
+ public DexType[] rewriteParameters(ProgramMethod method, DexItemFactory dexItemFactory) {
+ DexType[] params = method.getParameters().values;
+ if (isEmpty()) {
+ return params;
+ }
+ DexType[] newParams =
+ new DexType
+ [params.length
+ - argumentInfoCollection.numberOfRemovedNonReceiverArguments(method)
+ + extraParameters.size()];
+ int offset = method.getDefinition().getFirstNonReceiverArgumentIndex();
+ int newParamIndex = 0;
+ for (int oldParamIndex = 0; oldParamIndex < params.length; oldParamIndex++) {
+ ArgumentInfo argInfo = argumentInfoCollection.getArgumentInfo(oldParamIndex + offset);
+ if (argInfo.isNone()) {
+ newParams[newParamIndex++] = params[oldParamIndex];
+ } else if (argInfo.isRewrittenTypeInfo()) {
+ RewrittenTypeInfo rewrittenTypeInfo = argInfo.asRewrittenTypeInfo();
+ assert params[oldParamIndex] == rewrittenTypeInfo.oldType;
+ newParams[newParamIndex++] = rewrittenTypeInfo.newType;
+ }
+ }
+ for (ExtraParameter extraParameter : extraParameters) {
+ newParams[newParamIndex++] = extraParameter.getType(dexItemFactory);
+ }
+ return newParams;
+ }
+
public RewrittenPrototypeDescription rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
ArgumentInfoCollection newArgumentInfoCollection =
- argumentInfoCollection.rewrittenWithLens(appView, graphLens);
+ argumentInfoCollection.rewrittenWithLens(appView, graphLens, codeLens);
RewrittenTypeInfo newRewrittenReturnInfo =
- hasRewrittenReturnInfo() ? rewrittenReturnInfo.rewrittenWithLens(appView, graphLens) : null;
+ hasRewrittenReturnInfo()
+ ? rewrittenReturnInfo.rewrittenWithLens(appView, graphLens, codeLens)
+ : null;
if (newArgumentInfoCollection != argumentInfoCollection
|| newRewrittenReturnInfo != rewrittenReturnInfo) {
return new RewrittenPrototypeDescription(
@@ -844,6 +852,10 @@
return withExtraParameters(parameters);
}
+ public RewrittenPrototypeDescription withExtraParameters(ExtraParameter... parameters) {
+ return withExtraParameters(Arrays.asList(parameters));
+ }
+
public RewrittenPrototypeDescription withExtraParameters(List<ExtraParameter> parameters) {
if (parameters.isEmpty()) {
return this;
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
index 2fa3dfb..0800ede 100644
--- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -19,7 +19,6 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.function.Consumer;
-import java.util.function.Function;
public class SubtypingInfo {
@@ -29,119 +28,124 @@
// Since most Java types has no sub-types, we can just share an empty immutable set until we
// need to add to it.
private static final Set<DexType> NO_DIRECT_SUBTYPE = ImmutableSet.of();
- // Map from types to their subtyping information.
- private final DexItemFactory factory;
-
- private final Map<DexType, TypeInfo> typeInfo = new ConcurrentHashMap<>();
-
// Map from types to their subtypes.
- private final Map<DexType, ImmutableSet<DexType>> subtypeMap = new IdentityHashMap<>();
+ private final Map<DexType, Set<DexType>> subtypeMap;
- public SubtypingInfo(AppView<? extends AppInfoWithClassHierarchy> appView) {
- this(appView.appInfo());
+ private final Map<DexType, TypeInfo> typeInfo;
+
+ private SubtypingInfo(Map<DexType, TypeInfo> typeInfo, Map<DexType, Set<DexType>> subtypeMap) {
+ this.typeInfo = typeInfo;
+ this.subtypeMap = subtypeMap;
}
- public SubtypingInfo(AppInfoWithClassHierarchy appInfo) {
- this(appInfo.app().asDirect().allClasses(), appInfo);
+ public static SubtypingInfo create(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return create(appView.appInfo());
}
- private SubtypingInfo(Collection<DexClass> classes, DexDefinitionSupplier definitions) {
- factory = definitions.dexItemFactory();
- // Recompute subtype map if we have modified the graph.
- populateSubtypeMap(classes, definitions::definitionFor, factory);
+ public static SubtypingInfo create(AppInfoWithClassHierarchy appInfo) {
+ return create(appInfo.app().asDirect().allClasses(), appInfo);
}
- private void populateSuperType(
+ private static SubtypingInfo create(
+ Collection<DexClass> classes, DexDefinitionSupplier definitions) {
+ Map<DexType, TypeInfo> typeInfo = new ConcurrentHashMap<>();
+ Map<DexType, Set<DexType>> subtypeMap = new IdentityHashMap<>();
+ populateSubtypeMap(classes, subtypeMap, typeInfo, definitions);
+ return new SubtypingInfo(typeInfo, subtypeMap);
+ }
+
+ private static void populateSuperType(
Map<DexType, Set<DexType>> map,
+ Map<DexType, TypeInfo> typeInfo,
DexType superType,
DexClass baseClass,
- Function<DexType, DexClass> definitions) {
+ DexDefinitionSupplier definitionSupplier) {
if (superType != null) {
Set<DexType> set = map.computeIfAbsent(superType, ignore -> new HashSet<>());
if (set.add(baseClass.type)) {
// Only continue recursion if type has been added to set.
- populateAllSuperTypes(map, superType, baseClass, definitions);
+ populateAllSuperTypes(map, typeInfo, superType, baseClass, definitionSupplier);
}
}
}
- private DexItemFactory dexItemFactory() {
- return factory;
+ private TypeInfo getTypeInfo(DexType type) {
+ return getTypeInfo(type, typeInfo);
}
- private TypeInfo getTypeInfo(DexType type) {
+ private static TypeInfo getTypeInfo(DexType type, Map<DexType, TypeInfo> typeInfo) {
assert type != null;
return typeInfo.computeIfAbsent(type, TypeInfo::new);
}
- private void populateAllSuperTypes(
+ private static void populateAllSuperTypes(
Map<DexType, Set<DexType>> map,
+ Map<DexType, TypeInfo> typeInfo,
DexType holder,
DexClass baseClass,
- Function<DexType, DexClass> definitions) {
- DexClass holderClass = definitions.apply(holder);
+ DexDefinitionSupplier definitionSupplier) {
+ DexClass holderClass = definitionSupplier.contextIndependentDefinitionFor(holder);
// Skip if no corresponding class is found.
+ TypeInfo typeInfoHere = getTypeInfo(holder, typeInfo);
if (holderClass != null) {
- populateSuperType(map, holderClass.superType, baseClass, definitions);
- if (holderClass.superType != null) {
- getTypeInfo(holderClass.superType).addDirectSubtype(getTypeInfo(holder));
- } else {
- // We found java.lang.Object
- assert dexItemFactory().objectType == holder;
- }
- for (DexType inter : holderClass.interfaces.values) {
- populateSuperType(map, inter, baseClass, definitions);
- getTypeInfo(inter).addInterfaceSubtype(holder);
- }
+ holderClass.forEachImmediateSupertype(
+ (superType, isInterface) -> {
+ populateSuperType(map, typeInfo, superType, baseClass, definitionSupplier);
+ TypeInfo superTypeInfo = getTypeInfo(superType, typeInfo);
+ if (isInterface) {
+ superTypeInfo.addInterfaceSubtype(holder);
+ } else {
+ superTypeInfo.addDirectSubtype(typeInfoHere);
+ }
+ });
if (holderClass.isInterface()) {
- getTypeInfo(holder).tagAsInterface();
+ typeInfoHere.tagAsInterface();
}
} else {
// The subtype chain is broken, at least make this type a subtype of Object.
- if (holder != dexItemFactory().objectType) {
- getTypeInfo(dexItemFactory().objectType).addDirectSubtype(getTypeInfo(holder));
+ DexType objectType = definitionSupplier.dexItemFactory().objectType;
+ if (holder != objectType) {
+ getTypeInfo(objectType, typeInfo).addDirectSubtype(typeInfoHere);
}
}
}
- private void populateSubtypeMap(
+ private static void populateSubtypeMap(
Collection<DexClass> classes,
- Function<DexType, DexClass> definitions,
- DexItemFactory dexItemFactory) {
- getTypeInfo(dexItemFactory.objectType).tagAsSubtypeRoot();
- Map<DexType, Set<DexType>> map = new IdentityHashMap<>();
+ Map<DexType, Set<DexType>> map,
+ Map<DexType, TypeInfo> typeInfo,
+ DexDefinitionSupplier definitionSupplier) {
+ getTypeInfo(definitionSupplier.dexItemFactory().objectType, typeInfo).tagAsSubtypeRoot();
for (DexClass clazz : classes) {
- populateAllSuperTypes(map, clazz.type, clazz, definitions);
+ populateAllSuperTypes(map, typeInfo, clazz.type, clazz, definitionSupplier);
}
- for (Map.Entry<DexType, Set<DexType>> entry : map.entrySet()) {
- subtypeMap.put(entry.getKey(), ImmutableSet.copyOf(entry.getValue()));
- }
- assert validateLevelsAreCorrect(definitions, dexItemFactory);
+ map.replaceAll((k, v) -> ImmutableSet.copyOf(v));
+ assert validateLevelsAreCorrect(typeInfo, definitionSupplier);
}
- private boolean validateLevelsAreCorrect(
- Function<DexType, DexClass> definitions, DexItemFactory dexItemFactory) {
+ private static boolean validateLevelsAreCorrect(
+ Map<DexType, TypeInfo> typeInfo, DexDefinitionSupplier definitionSupplier) {
Set<DexType> seenTypes = Sets.newIdentityHashSet();
Deque<DexType> worklist = new ArrayDeque<>();
- DexType objectType = dexItemFactory.objectType;
+ DexType objectType = definitionSupplier.dexItemFactory().objectType;
worklist.add(objectType);
while (!worklist.isEmpty()) {
DexType next = worklist.pop();
- DexClass nextHolder = definitions.apply(next);
+ DexClass nextHolder = definitionSupplier.contextIndependentDefinitionFor(next);
DexType superType;
if (nextHolder == null) {
// We might lack the definition of Object, so guard against that.
- superType = next == dexItemFactory.objectType ? null : dexItemFactory.objectType;
+ superType = next == objectType ? null : objectType;
} else {
superType = nextHolder.superType;
}
assert !seenTypes.contains(next);
seenTypes.add(next);
- TypeInfo nextInfo = getTypeInfo(next);
+ TypeInfo nextInfo = getTypeInfo(next, typeInfo);
if (superType == null) {
assert nextInfo.hierarchyLevel == ROOT_LEVEL;
} else {
- TypeInfo superInfo = getTypeInfo(superType);
+ TypeInfo superInfo = getTypeInfo(superType, typeInfo);
assert superInfo.hierarchyLevel == nextInfo.hierarchyLevel - 1
|| (superInfo.hierarchyLevel == ROOT_LEVEL
&& nextInfo.hierarchyLevel == INTERFACE_LEVEL);
@@ -153,7 +157,7 @@
} else if (nextHolder != null) {
// Test that the interfaces of this class are interfaces and have this class as subtype.
for (DexType iface : nextHolder.interfaces.values) {
- TypeInfo ifaceInfo = getTypeInfo(iface);
+ TypeInfo ifaceInfo = getTypeInfo(iface, typeInfo);
assert ifaceInfo.directSubtypes.contains(next);
assert ifaceInfo.hierarchyLevel == INTERFACE_LEVEL;
}
@@ -164,7 +168,7 @@
public Set<DexType> subtypes(DexType type) {
assert type.isClassType();
- ImmutableSet<DexType> subtypes = subtypeMap.get(type);
+ Set<DexType> subtypes = subtypeMap.get(type);
return subtypes == null ? ImmutableSet.of() : subtypes;
}
diff --git a/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java
index 613db50..af17ea3 100644
--- a/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java
+++ b/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java
@@ -74,7 +74,7 @@
// Add super classes to worklist.
if (clazz.superType != null) {
- DexClass definition = appView.definitionFor(clazz.superType);
+ DexClass definition = definitionSupplier.contextIndependentDefinitionFor(clazz.superType);
if (definition != null && shouldTraverseUpwardsFrom(definition)) {
addDependentsToWorklist(definition);
}
@@ -83,7 +83,7 @@
// Add super interfaces to worklist.
if (!excludeInterfaces) {
for (DexType interfaceType : clazz.interfaces.values) {
- DexClass definition = appView.definitionFor(interfaceType);
+ DexClass definition = definitionSupplier.contextIndependentDefinitionFor(interfaceType);
if (definition != null && shouldTraverseUpwardsFrom(definition)) {
addDependentsToWorklist(definition);
}
diff --git a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
index 16427bf..f9e2af2 100644
--- a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
+++ b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
@@ -5,12 +5,14 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
public abstract class TreeFixerBase {
@@ -165,21 +167,28 @@
/** Fixup a list of fields. */
public DexEncodedField[] fixupFields(List<DexEncodedField> fields) {
+ return fixupFields(fields, ConsumerUtils.emptyConsumer());
+ }
+
+ public DexEncodedField[] fixupFields(
+ List<DexEncodedField> fields, Consumer<DexEncodedField.Builder> consumer) {
if (fields == null) {
return DexEncodedField.EMPTY_ARRAY;
}
DexEncodedField[] newFields = new DexEncodedField[fields.size()];
for (int i = 0; i < fields.size(); i++) {
- newFields[i] = fixupField(fields.get(i));
+ newFields[i] = fixupField(fields.get(i), consumer);
}
return newFields;
}
- private DexEncodedField fixupField(DexEncodedField field) {
+ private DexEncodedField fixupField(
+ DexEncodedField field, Consumer<DexEncodedField.Builder> consumer) {
DexField fieldReference = field.getReference();
DexField newFieldReference = fixupFieldReference(fieldReference);
if (newFieldReference != fieldReference) {
- return recordFieldChange(field, field.toTypeSubstitutedField(appView, newFieldReference));
+ return recordFieldChange(
+ field, field.toTypeSubstitutedField(appView, newFieldReference, consumer));
}
return field;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 62b09a6..b18a69c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -193,6 +193,11 @@
representative = ListUtils.first(methods);
}
+ if (representative.getAccessFlags().isAbstract() && superMethod != null) {
+ methods.forEach(method -> lensBuilder.mapMethod(method.getReference(), newMethodReference));
+ return;
+ }
+
for (ProgramMethod method : methods) {
if (method.getReference() == representative.getReference()) {
lensBuilder.moveMethod(method.getReference(), newMethodReference);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
index 5ebe1a6..deb0e02 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
@@ -146,7 +146,7 @@
@Override
public Map<DexType, InterfaceInfo> preprocess(
Collection<MergeGroup> groups, ExecutorService executorService) {
- SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
+ SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
Collection<DexProgramClass> classesOfInterest = computeClassesOfInterest(subtypingInfo);
Map<DexType, DexMethodSignatureSet> inheritedClassMethodsPerClass =
computeInheritedClassMethodsPerProgramClass(classesOfInterest);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
index f86a398..6d54364 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
@@ -162,7 +162,7 @@
@Override
public SubtypingInfo preprocess(Collection<MergeGroup> groups, ExecutorService executorService) {
- return new SubtypingInfo(appView);
+ return SubtypingInfo.create(appView);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
index b6ac534..58033b1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
@@ -77,5 +77,5 @@
AppView<AppInfoWithLiveness> appView, ArgumentInfoCollection argumentInfoCollection);
public abstract AbstractFieldSet rewrittenWithLens(
- AppView<?> appView, GraphLens lens, PrunedItems prunedItems);
+ AppView<?> appView, GraphLens lens, GraphLens codeLens, PrunedItems prunedItems);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index 757e426..84208bb 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -89,7 +89,7 @@
@Override
public AbstractFieldSet rewrittenWithLens(
- AppView<?> appView, GraphLens lens, PrunedItems prunedItems) {
+ AppView<?> appView, GraphLens lens, GraphLens codeLens, PrunedItems prunedItems) {
assert !isEmpty();
ConcreteMutableFieldSet rewrittenSet = new ConcreteMutableFieldSet();
for (DexEncodedField field : fields) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
index 78fa9c0..65e424f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
@@ -73,7 +73,7 @@
@Override
public AbstractFieldSet rewrittenWithLens(
- AppView<?> appView, GraphLens lens, PrunedItems prunedItems) {
+ AppView<?> appView, GraphLens lens, GraphLens codeLens, PrunedItems prunedItems) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
index 5de960d..c9dcd7a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
@@ -44,7 +44,7 @@
@Override
public AbstractFieldSet rewrittenWithLens(
- AppView<?> appView, GraphLens lens, PrunedItems prunedItems) {
+ AppView<?> appView, GraphLens lens, GraphLens codeLens, PrunedItems prunedItems) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
index 88d8b66..0ccf414 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Phi;
@@ -23,8 +24,11 @@
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Function<DexType, DexType> mapping;
- public DestructivePhiTypeUpdater(AppView<? extends AppInfoWithClassHierarchy> appView) {
- this(appView, appView.graphLens()::lookupType);
+ public DestructivePhiTypeUpdater(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ GraphLens graphLens,
+ GraphLens codeLens) {
+ this(appView, type -> graphLens.lookupType(type, codeLens));
}
public DestructivePhiTypeUpdater(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithUpperBound.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithUpperBound.java
index 0ce069a..754fb43 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithUpperBound.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithUpperBound.java
@@ -207,11 +207,11 @@
return this;
}
TypeElement rewrittenDynamicUpperBoundType =
- dynamicUpperBoundType.rewrittenWithLens(appView, graphLens, prunedTypes);
+ dynamicUpperBoundType.rewrittenWithLens(appView, graphLens, null, prunedTypes);
ClassTypeElement rewrittenDynamicLowerBoundClassType = null;
if (hasDynamicLowerBoundType()) {
TypeElement rewrittenDynamicLowerBoundType =
- getDynamicLowerBoundType().rewrittenWithLens(appView, graphLens, prunedTypes);
+ getDynamicLowerBoundType().rewrittenWithLens(appView, graphLens, null, prunedTypes);
if (rewrittenDynamicLowerBoundType.isClassType()) {
rewrittenDynamicLowerBoundClassType = rewrittenDynamicLowerBoundType.asClassType();
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
index 1ab6c53..8d2db28 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
@@ -45,7 +45,7 @@
public DynamicType rewrittenWithLens(
AppView<AppInfoWithLiveness> appView, GraphLens graphLens, Set<DexType> prunedTypes) {
TypeElement rewrittenType =
- getExactClassType().rewrittenWithLens(appView, graphLens, prunedTypes);
+ getExactClassType().rewrittenWithLens(appView, graphLens, null, prunedTypes);
assert rewrittenType.isClassType() || rewrittenType.isPrimitiveType();
return rewrittenType.isClassType()
? new ExactDynamicType(rewrittenType.asClassType())
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index e97006c..6451a1c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.ir.code.Value;
+import java.util.Collections;
import java.util.Set;
import java.util.function.Function;
@@ -85,14 +86,23 @@
public final TypeElement rewrittenWithLens(
AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens graphLens) {
- return rewrittenWithLens(appView, graphLens, emptySet());
+ return rewrittenWithLens(appView, graphLens, null);
}
public final TypeElement rewrittenWithLens(
AppView<? extends AppInfoWithClassHierarchy> appView,
GraphLens graphLens,
+ GraphLens codeLens) {
+ return rewrittenWithLens(appView, graphLens, codeLens, Collections.emptySet());
+ }
+
+ public final TypeElement rewrittenWithLens(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ GraphLens graphLens,
+ GraphLens codeLens,
Set<DexType> prunedTypes) {
- return fixupClassTypeReferences(appView, graphLens::lookupType, prunedTypes);
+ return fixupClassTypeReferences(
+ appView, type -> graphLens.lookupType(type, codeLens), prunedTypes);
}
public boolean isNullable() {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index b9d02f9..d62f8e7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -244,7 +244,7 @@
}
public abstract AbstractValue rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens);
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens);
@Override
public abstract boolean equals(Object o);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java
index c1afe58..4253a3d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java
@@ -24,7 +24,8 @@
}
@Override
- public AbstractValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public AbstractValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java
index 2bf329d..db25101 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java
@@ -44,8 +44,8 @@
@Override
public NullOrAbstractValue rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
- return new NullOrAbstractValue(value.rewrittenWithLens(appView, lens));
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
+ return new NullOrAbstractValue(value.rewrittenWithLens(appView, lens, codeLens));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
index f100893..3238680 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
@@ -73,7 +73,8 @@
}
@Override
- public AbstractValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public AbstractValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java
index e6062f6..1c539c9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java
@@ -87,7 +87,8 @@
}
@Override
- public AbstractValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public AbstractValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
index 911bddb..ad8b4d5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
@@ -105,7 +105,7 @@
public boolean isMaterializableInAllContexts(AppView<AppInfoWithLiveness> appView) {
DexType baseType = type.toBaseType(appView.dexItemFactory());
if (baseType.isClassType()) {
- DexClass clazz = appView.definitionFor(type);
+ DexClass clazz = appView.definitionFor(baseType);
if (clazz == null || !clazz.isPublic() || !clazz.isResolvable(appView)) {
return false;
}
@@ -128,8 +128,9 @@
}
@Override
- public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
- assert lens.lookupType(type) == type;
+ public SingleValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
+ assert lens.lookupType(type, codeLens) == type;
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
index f1fac36..4f4f7cd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
@@ -114,9 +114,11 @@
}
@Override
- public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public SingleValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return appView
.abstractValueFactory()
- .createSingleDexItemBasedStringValue(lens.rewriteReference(item), nameComputationInfo);
+ .createSingleDexItemBasedStringValue(
+ lens.rewriteReference(item, codeLens), nameComputationInfo);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index 0e746d4..cbc7e66 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -135,7 +135,8 @@
}
@Override
- public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public SingleValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
AbstractValueFactory factory = appView.abstractValueFactory();
if (field.holder == field.type) {
EnumDataMap enumDataMap = appView.unboxedEnums();
@@ -143,8 +144,8 @@
return factory.createSingleNumberValue(enumDataMap.getUnboxedValue(field));
}
}
- DexField rewrittenField = lens.lookupField(field);
- ObjectState rewrittenObjectState = getObjectState().rewrittenWithLens(appView, lens);
+ DexField rewrittenField = lens.lookupField(field, codeLens);
+ ObjectState rewrittenObjectState = getObjectState().rewrittenWithLens(appView, lens, codeLens);
if (rewrittenField != field || rewrittenObjectState != getObjectState()) {
return factory.createSingleFieldValue(rewrittenField, rewrittenObjectState);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index 264bec5..b3259b0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -160,7 +160,8 @@
}
@Override
- public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public SingleValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
index f346b24..57cd2e1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
@@ -101,7 +101,8 @@
}
@Override
- public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public SingleValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
index 94cd837..390ac7b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
@@ -56,5 +56,5 @@
@Override
public abstract SingleValue rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens);
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java
index 39174da..edcfbc6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java
@@ -50,8 +50,9 @@
}
@Override
- public AbstractValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
- return create(getObjectState().rewrittenWithLens(appView, lens));
+ public AbstractValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
+ return create(getObjectState().rewrittenWithLens(appView, lens, codeLens));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java
index 8d4001f..8faaea4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java
@@ -29,7 +29,8 @@
}
@Override
- public AbstractValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public AbstractValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EmptyObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EmptyObjectState.java
index 1b94542..0d1255e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EmptyObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EmptyObjectState.java
@@ -39,7 +39,8 @@
}
@Override
- public ObjectState rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public ObjectState rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
index 817b166..8311d1e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
@@ -71,10 +71,11 @@
}
@Override
- public ObjectState rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public ObjectState rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
ObjectState[] newState = new ObjectState[state.length];
for (int i = 0; i < state.length; i++) {
- newState[i] = state[i].rewrittenWithLens(appView, lens);
+ newState[i] = state[i].rewrittenWithLens(appView, lens, codeLens);
}
return new EnumValuesObjectState(newState);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/KnownLengthArrayState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/KnownLengthArrayState.java
index a379776..8c9e449 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/KnownLengthArrayState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/KnownLengthArrayState.java
@@ -47,7 +47,8 @@
}
@Override
- public ObjectState rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public ObjectState rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java
index 559fa77..7133b9c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/NonEmptyObjectState.java
@@ -42,11 +42,14 @@
}
@Override
- public ObjectState rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ public ObjectState rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
Map<DexField, AbstractValue> rewrittenState = new IdentityHashMap<>();
state.forEach(
(field, value) ->
- rewrittenState.put(lens.lookupField(field), value.rewrittenWithLens(appView, lens)));
+ rewrittenState.put(
+ lens.lookupField(field, codeLens),
+ value.rewrittenWithLens(appView, lens, codeLens)));
return new NonEmptyObjectState(rewrittenState);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java
index b81fb32..bc0762a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java
@@ -51,7 +51,7 @@
public abstract boolean isEmpty();
public abstract ObjectState rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens);
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens);
@Override
public abstract boolean equals(Object o);
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 90ca265..e5f15bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -1032,13 +1033,13 @@
*
* @return true if any guards were renamed.
*/
- public boolean renameGuardsInCatchHandlers(GraphLens graphLens) {
+ public boolean renameGuardsInCatchHandlers(NonIdentityGraphLens graphLens, GraphLens codeLens) {
assert hasCatchHandlers();
boolean anyGuardsRenamed = false;
List<DexType> newGuards = new ArrayList<>(catchHandlers.getGuards().size());
for (DexType guard : catchHandlers.getGuards()) {
// The type may have changed due to class merging.
- DexType renamed = graphLens.lookupType(guard);
+ DexType renamed = graphLens.lookupType(guard, codeLens);
newGuards.add(renamed);
anyGuardsRenamed |= renamed != guard;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 2ad1f65..e1ed280 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -34,6 +34,7 @@
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
+import java.util.function.Consumer;
public class BasicBlockInstructionListIterator implements InstructionListIterator {
@@ -350,8 +351,11 @@
}
@Override
- public boolean replaceCurrentInstructionByInitClassIfPossible(
- AppView<AppInfoWithLiveness> appView, IRCode code, DexType type) {
+ public boolean removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ DexType type,
+ Consumer<InitClass> consumer) {
Instruction toBeReplaced = current;
assert toBeReplaced != null;
assert toBeReplaced.isStaticFieldInstruction() || toBeReplaced.isInvokeStatic();
@@ -377,7 +381,9 @@
DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
if (clazz != null) {
Value dest = code.createValue(TypeElement.getInt());
- replaceCurrentInstruction(new InitClass(dest, clazz.type));
+ InitClass initClass = new InitClass(dest, clazz.type);
+ replaceCurrentInstruction(initClass);
+ consumer.accept(initClass);
}
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
index 64b59e4..d81f466 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
@@ -355,6 +355,11 @@
}
@Override
+ public T visit(UnusedArgument instruction) {
+ return null;
+ }
+
+ @Override
public T visit(Ushr instruction) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index f4a2cdc..e44b158 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -1231,17 +1232,29 @@
for (BasicBlock block : blocks) {
List<Phi> phis = new ArrayList<>(block.getPhis());
for (Phi phi : phis) {
- if (phi.hasAnyUsers()) {
- anyPhisRemoved |= phi.removeTrivialPhi(builder, affectedValues);
- } else {
- phi.removeDeadPhi();
+ WorkList<Phi> reachablePhis = WorkList.newIdentityWorkList(phi);
+ if (isDeadPhi(reachablePhis)) {
+ reachablePhis.getSeenSet().forEach(Phi::removeDeadPhi);
anyPhisRemoved = true;
+ } else {
+ anyPhisRemoved |= phi.removeTrivialPhi(builder, affectedValues);
}
}
}
return anyPhisRemoved;
}
+ private boolean isDeadPhi(WorkList<Phi> reachablePhis) {
+ while (reachablePhis.hasNext()) {
+ Phi currentPhi = reachablePhis.next();
+ if (currentPhi.hasUsers() || currentPhi.hasDebugUsers()) {
+ return false;
+ }
+ reachablePhis.addIfNotSeen(currentPhi.uniquePhiUsers());
+ }
+ return true;
+ }
+
public int reserveMarkingColor() {
assert anyMarkingColorAvailable();
int color = 1;
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index a68da1a..21b9df5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -19,6 +19,7 @@
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.function.Consumer;
public class IRCodeInstructionListIterator implements InstructionListIterator {
@@ -62,9 +63,13 @@
}
@Override
- public boolean replaceCurrentInstructionByInitClassIfPossible(
- AppView<AppInfoWithLiveness> appView, IRCode code, DexType type) {
- return instructionIterator.replaceCurrentInstructionByInitClassIfPossible(appView, code, type);
+ public boolean removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ DexType type,
+ Consumer<InitClass> consumer) {
+ return instructionIterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ appView, code, type, consumer);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 30f9c5f..ec25297 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -748,6 +748,14 @@
return null;
}
+ public boolean isUnusedArgument() {
+ return false;
+ }
+
+ public UnusedArgument asUnusedArgument() {
+ return null;
+ }
+
public boolean isArithmeticBinop() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index bc6bec6..f1eab89 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -15,10 +15,12 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.util.ListIterator;
import java.util.Set;
+import java.util.function.Consumer;
public interface InstructionListIterator
extends InstructionIterator, ListIterator<Instruction>, PreviousUntilIterator<Instruction> {
@@ -109,8 +111,17 @@
boolean replaceCurrentInstructionByNullCheckIfPossible(AppView<?> appView, ProgramMethod context);
- boolean replaceCurrentInstructionByInitClassIfPossible(
- AppView<AppInfoWithLiveness> appView, IRCode code, DexType type);
+ default boolean removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView, IRCode code, DexType type) {
+ return removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ appView, code, type, ConsumerUtils.emptyConsumer());
+ }
+
+ boolean removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ DexType type,
+ Consumer<InitClass> consumer);
void replaceCurrentInstructionWithConstClass(
AppView<?> appView, IRCode code, DexType type, DebugLocalInfo localInfo);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
index ddb611f..0fe8c68 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
@@ -144,6 +144,8 @@
T visit(Throw instruction);
+ T visit(UnusedArgument instruction);
+
T visit(Ushr instruction);
T visit(Xor instruction);
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index 37d45df..ffb5d2b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -18,6 +18,7 @@
import com.google.common.collect.Sets;
import java.util.ListIterator;
import java.util.Set;
+import java.util.function.Consumer;
public class LinearFlowInstructionListIterator implements InstructionListIterator {
@@ -86,9 +87,13 @@
}
@Override
- public boolean replaceCurrentInstructionByInitClassIfPossible(
- AppView<AppInfoWithLiveness> appView, IRCode code, DexType type) {
- return currentBlockIterator.replaceCurrentInstructionByInitClassIfPossible(appView, code, type);
+ public boolean removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ DexType type,
+ Consumer<InitClass> consumer) {
+ return currentBlockIterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ appView, code, type, consumer);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Opcodes.java b/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
index f1527a6..eb6eb85 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
@@ -72,8 +72,9 @@
int SUB = 63;
int SWAP = 64;
int THROW = 65;
- int USHR = 66;
- int XOR = 67;
- int UNINITIALIZED_THIS_LOCAL_READ = 68;
- int RECORD_FIELD_VALUES = 69;
+ int UNUSED_ARGUMENT = 66;
+ int USHR = 67;
+ int XOR = 68;
+ int UNINITIALIZED_THIS_LOCAL_READ = 69;
+ int RECORD_FIELD_VALUES = 70;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/UnusedArgument.java b/src/main/java/com/android/tools/r8/ir/code/UnusedArgument.java
new file mode 100644
index 0000000..29fc073
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/UnusedArgument.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2022, 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.code;
+
+import com.android.tools.r8.cf.LoadStoreHelper;
+import com.android.tools.r8.cf.TypeVerificationHelper;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
+
+/**
+ * A special instruction to load the value of an argument that has been removed as a result of code
+ * optimizations. Only used in R8 between the point of building IR and lens code rewriting.
+ */
+public class UnusedArgument extends Instruction {
+
+ public UnusedArgument(Value outValue) {
+ super(outValue);
+ }
+
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public DeadInstructionResult canBeDeadCode(AppView<?> appview, IRCode code) {
+ return DeadInstructionResult.deadIfOutValueIsDead();
+ }
+
+ @Override
+ public DexType computeVerificationType(AppView<?> appView, TypeVerificationHelper helper) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public TypeElement evaluate(AppView<?> appView) {
+ return outValue.getType();
+ }
+
+ @Override
+ public boolean hasInvariantOutType() {
+ return true;
+ }
+
+ @Override
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public ConstraintWithTarget inliningConstraint(
+ InliningConstraints inliningConstraints, ProgramMethod context) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean isUnusedArgument() {
+ return true;
+ }
+
+ @Override
+ public UnusedArgument asUnusedArgument() {
+ return this;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int opcode() {
+ return Opcodes.UNUSED_ARGUMENT;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java
index b0544d9..ec2df95 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.conversion;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
@@ -18,6 +19,11 @@
}
@Override
+ public DexType getType(DexItemFactory dexItemFactory) {
+ return dexItemFactory.intType;
+ }
+
+ @Override
public TypeElement getTypeElement(AppView<?> appView, DexType argType) {
assert argType == appView.dexItemFactory().intType;
return TypeElement.getInt();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java
index 6211fa3..44452f6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java
@@ -5,12 +5,15 @@
package com.android.tools.r8.ir.conversion;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
public abstract class ExtraParameter {
+ public abstract DexType getType(DexItemFactory dexItemFactory);
+
public abstract TypeElement getTypeElement(AppView<?> appView, DexType argType);
public abstract SingleNumberValue getValue(AppView<?> appView);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
index a543a0c..0532a48 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.conversion;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -12,6 +13,23 @@
public class ExtraUnusedNullParameter extends ExtraParameter {
+ private final DexType type;
+
+ @Deprecated
+ public ExtraUnusedNullParameter() {
+ this(null);
+ }
+
+ public ExtraUnusedNullParameter(DexType type) {
+ this.type = type;
+ }
+
+ @Override
+ public DexType getType(DexItemFactory dexItemFactory) {
+ assert type != null;
+ return type;
+ }
+
@Override
public TypeElement getTypeElement(AppView<?> appView, DexType argType) {
return TypeElement.fromDexType(argType, Nullability.maybeNull(), appView);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 5f318cd..133023f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -45,7 +45,6 @@
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.Add;
import com.android.tools.r8.ir.code.And;
import com.android.tools.r8.ir.code.Argument;
@@ -114,7 +113,6 @@
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Sub;
import com.android.tools.r8.ir.code.Throw;
-import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
import com.android.tools.r8.ir.code.Ushr;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
@@ -405,8 +403,6 @@
private Value receiverValue;
private List<Value> argumentValues;
- private List<Instruction> pendingArgumentInstructions;
-
// Source code to build IR from. Null if already built.
private SourceCode source;
@@ -520,7 +516,6 @@
}
public List<Value> getArgumentValues() {
- assert pendingArgumentInstructions == null || pendingArgumentInstructions.isEmpty();
return argumentValues;
}
@@ -570,7 +565,7 @@
type =
TypeElement.fromDexType(
removedArgumentInfo.getType(), Nullability.maybeNull(), appView);
- addConstantOrUnusedArgument(register, removedArgumentInfo);
+ addNonThisArgument(register, type);
} else {
DexType argType;
if (argumentInfo.isRewrittenTypeInfo()) {
@@ -603,14 +598,11 @@
register += type.requiredRegisters();
usedArgumentIndex++;
if (extraParameter instanceof ExtraUnusedNullParameter) {
- assert argType.isClassType();
- addExtraUnusedNullArgument(register);
+ addExtraUnusedArgument(register, argType);
} else {
addNonThisArgument(register, type);
}
}
-
- flushArgumentInstructions();
}
/**
@@ -996,11 +988,15 @@
value.markAsThis();
}
- private void addExtraUnusedNullArgument(int register) {
+ private void addExtraUnusedArgument(int register, DexType type) {
// Extra unused null arguments should bypass the register check, they may use registers
// beyond the limit of what the method can use. They don't have debug information and are
// always null.
- Value value = new Value(valueNumberGenerator.next(), TypeElement.getNull(), null);
+ Value value =
+ new Value(
+ valueNumberGenerator.next(),
+ type.isReferenceType() ? TypeElement.getNull() : type.toTypeElement(appView),
+ null);
addNonThisArgument(new Argument(value, currentBlock.size(), false));
}
@@ -1024,46 +1020,6 @@
argumentValues.add(argument.outValue());
}
- public void addConstantOrUnusedArgument(int register, RemovedArgumentInfo info) {
- handleConstantOrUnusedArgument(register, info);
- }
-
- private void handleConstantOrUnusedArgument(
- int register, RemovedArgumentInfo removedArgumentInfo) {
- assert removedArgumentInfo != null;
- if (removedArgumentInfo.hasSingleValue()) {
- if (pendingArgumentInstructions == null) {
- pendingArgumentInstructions = new ArrayList<>();
- }
- DebugLocalInfo local = getOutgoingLocal(register);
- SingleValue singleValue = removedArgumentInfo.getSingleValue();
- TypeElement type =
- removedArgumentInfo.getType().isReferenceType() && singleValue.isNull()
- ? getNull()
- : removedArgumentInfo.getType().toTypeElement(appView);
- Instruction materializingInstruction =
- singleValue.createMaterializingInstruction(
- appView.withClassHierarchy(),
- method,
- valueNumberGenerator,
- TypeAndLocalInfoSupplier.create(type, local));
- writeRegister(
- register,
- materializingInstruction.outValue(),
- ThrowingInfo.defaultForInstruction(materializingInstruction));
- pendingArgumentInstructions.add(materializingInstruction);
- } else {
- assert removedArgumentInfo.isNeverUsed();
- }
- }
-
- public void flushArgumentInstructions() {
- if (pendingArgumentInstructions != null) {
- pendingArgumentInstructions.forEach(this::addInstruction);
- pendingArgumentInstructions.clear();
- }
- }
-
private static boolean isValidFor(Value value, DebugLocalInfo local) {
// Invalid debug-info may cause attempt to read a local that is not actually alive.
// See b/37722432 and regression test {@code jasmin.InvalidDebugInfoTests::testInvalidInfoThrow}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index e5602b2..a52190d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -34,6 +34,7 @@
import static com.android.tools.r8.utils.ObjectUtils.getBooleanOrElse;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -50,16 +51,19 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.FieldLookupResult;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
@@ -82,25 +86,30 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMultiNewArray;
import com.android.tools.r8.ir.code.InvokeNewArray;
-import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.MoveException;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SourcePosition;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.SafeCheckCast;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
+import com.android.tools.r8.ir.code.UnusedArgument;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.LazyBox;
import com.android.tools.r8.verticalclassmerging.InterfaceTypeToClassTypeLensCodeRewriterHelper;
+import com.google.common.collect.Sets;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
@@ -110,30 +119,48 @@
public class LensCodeRewriter {
+ private static class GraphLensInterval {
+
+ private final NonIdentityGraphLens graphLens;
+ private final GraphLens codeLens;
+ private final DexMethod method;
+
+ GraphLensInterval(NonIdentityGraphLens graphLens, GraphLens codeLens, DexMethod method) {
+ this.graphLens = graphLens;
+ this.codeLens = codeLens;
+ this.method = method;
+ }
+
+ public NonIdentityGraphLens getGraphLens() {
+ return graphLens;
+ }
+
+ public GraphLens getCodeLens() {
+ return codeLens;
+ }
+
+ public DexMethod getMethod() {
+ return method;
+ }
+ }
+
private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final DexItemFactory factory;
private final EnumUnboxer enumUnboxer;
- private LensCodeRewriterUtils helper = null;
private final InternalOptions options;
LensCodeRewriter(AppView<? extends AppInfoWithClassHierarchy> appView, EnumUnboxer enumUnboxer) {
this.appView = appView;
+ this.factory = appView.dexItemFactory();
this.enumUnboxer = enumUnboxer;
this.options = appView.options();
}
- private LensCodeRewriterUtils getHelper() {
- // The LensCodeRewriterUtils uses internal caches that are not valid if the graphLens changes.
- if (helper != null && helper.hasGraphLens(appView.graphLens())) {
- return helper;
- }
- helper = new LensCodeRewriterUtils(appView);
- return helper;
- }
-
- private Value makeOutValue(Instruction insn, IRCode code) {
+ private Value makeOutValue(
+ Instruction insn, IRCode code, NonIdentityGraphLens graphLens, GraphLens codeLens) {
if (insn.hasOutValue()) {
TypeElement oldType = insn.getOutType();
- TypeElement newType = oldType.rewrittenWithLens(appView, appView.graphLens());
+ TypeElement newType = oldType.rewrittenWithLens(appView, graphLens, codeLens);
return code.createValue(newType, insn.getLocalInfo());
}
return null;
@@ -150,21 +177,71 @@
/** Replace type appearances, invoke targets and field accesses with actual definitions. */
public void rewrite(IRCode code, ProgramMethod method, MethodProcessor methodProcessor) {
- Set<Phi> affectedPhis = enumUnboxer.rewriteCode(code, methodProcessor);
- GraphLens graphLens = appView.graphLens();
- DexItemFactory factory = appView.dexItemFactory();
+ Deque<GraphLensInterval> unappliedLenses = getUnappliedLenses(method);
+ DexMethod originalMethodReference =
+ appView.graphLens().getOriginalMethodSignature(method.getReference());
+ while (!unappliedLenses.isEmpty()) {
+ GraphLensInterval unappliedLens = unappliedLenses.removeLast();
+ RewrittenPrototypeDescription prototypeChanges =
+ unappliedLens
+ .getGraphLens()
+ .lookupPrototypeChangesForMethodDefinition(
+ unappliedLens.getMethod(), unappliedLens.getCodeLens());
+ rewritePartial(
+ code,
+ method,
+ originalMethodReference,
+ methodProcessor,
+ unappliedLens.getGraphLens(),
+ unappliedLens.getCodeLens(),
+ prototypeChanges);
+ }
+ assert code.hasNoMergedClasses(appView);
+ }
+
+ private void rewritePartial(
+ IRCode code,
+ ProgramMethod method,
+ DexMethod originalMethodReference,
+ MethodProcessor methodProcessor,
+ NonIdentityGraphLens graphLens,
+ GraphLens codeLens,
+ RewrittenPrototypeDescription prototypeChanges) {
// Rewriting types that affects phi can cause us to compute TOP for cyclic phi's. To solve this
// we track all phi's that needs to be re-computed.
+ Set<Phi> affectedPhis = Sets.newIdentityHashSet();
+ Set<UnusedArgument> unusedArguments = Sets.newIdentityHashSet();
+ rewriteArguments(
+ code, originalMethodReference, prototypeChanges, affectedPhis, unusedArguments);
+ if (graphLens.hasCustomCodeRewritings()) {
+ assert graphLens.isEnumUnboxerLens();
+ assert graphLens.getPrevious() == codeLens;
+ affectedPhis.addAll(enumUnboxer.rewriteCode(code, methodProcessor, prototypeChanges));
+ }
+ rewritePartialDefault(
+ code, method, graphLens, codeLens, prototypeChanges, affectedPhis, unusedArguments);
+ }
+
+ private void rewritePartialDefault(
+ IRCode code,
+ ProgramMethod method,
+ NonIdentityGraphLens graphLens,
+ GraphLens codeLens,
+ RewrittenPrototypeDescription prototypeChangesForMethod,
+ Set<Phi> affectedPhis,
+ Set<UnusedArgument> unusedArguments) {
BasicBlockIterator blocks = code.listIterator();
+ LazyBox<LensCodeRewriterUtils> helper =
+ new LazyBox<>(() -> new LensCodeRewriterUtils(appView, graphLens, codeLens));
InterfaceTypeToClassTypeLensCodeRewriterHelper interfaceTypeToClassTypeRewriterHelper =
- InterfaceTypeToClassTypeLensCodeRewriterHelper.create(appView, code, methodProcessor);
+ InterfaceTypeToClassTypeLensCodeRewriterHelper.create(appView, code, graphLens, codeLens);
boolean mayHaveUnreachableBlocks = false;
while (blocks.hasNext()) {
BasicBlock block = blocks.next();
if (block.hasCatchHandlers() && options.enableVerticalClassMerging) {
- boolean anyGuardsRenamed = block.renameGuardsInCatchHandlers(graphLens);
+ boolean anyGuardsRenamed = block.renameGuardsInCatchHandlers(graphLens, codeLens);
if (anyGuardsRenamed) {
- mayHaveUnreachableBlocks |= unlinkDeadCatchHandlers(block);
+ mayHaveUnreachableBlocks |= unlinkDeadCatchHandlers(block, graphLens, codeLens);
}
}
InstructionListIterator iterator = block.listIterator(code);
@@ -175,9 +252,9 @@
{
InvokeCustom invokeCustom = current.asInvokeCustom();
DexCallSite callSite = invokeCustom.getCallSite();
- DexCallSite newCallSite = getHelper().rewriteCallSite(callSite, method);
+ DexCallSite newCallSite = helper.computeIfAbsent().rewriteCallSite(callSite, method);
if (newCallSite != callSite) {
- Value newOutValue = makeOutValue(invokeCustom, code);
+ Value newOutValue = makeOutValue(invokeCustom, code, graphLens, codeLens);
InvokeCustom newInvokeCustom =
new InvokeCustom(newCallSite, newOutValue, invokeCustom.inValues());
iterator.replaceCurrentInstruction(newInvokeCustom);
@@ -192,10 +269,11 @@
{
DexMethodHandle handle = current.asConstMethodHandle().getValue();
DexMethodHandle newHandle =
- getHelper()
+ helper
+ .computeIfAbsent()
.rewriteDexMethodHandle(handle, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY, method);
if (newHandle != handle) {
- Value newOutValue = makeOutValue(current, code);
+ Value newOutValue = makeOutValue(current, code, graphLens, codeLens);
iterator.replaceCurrentInstruction(new ConstMethodHandle(newOutValue, newHandle));
if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
affectedPhis.addAll(newOutValue.uniquePhiUsers());
@@ -209,7 +287,10 @@
InitClass initClass = current.asInitClass();
new InstructionReplacer(code, current, iterator, affectedPhis)
.replaceInstructionIfTypeChanged(
- initClass.getClassValue(), (t, v) -> new InitClass(v, t));
+ initClass.getClassValue(),
+ (t, v) -> new InitClass(v, t),
+ graphLens,
+ codeLens);
}
break;
@@ -236,7 +317,9 @@
factory.createMethod(
mappedHolder, invokedMethod.proto, invokedMethod.name);
return Invoke.create(VIRTUAL, actualTarget, null, v, invoke.inValues());
- });
+ },
+ graphLens,
+ codeLens);
continue;
}
if (!invokedHolder.isClassType()) {
@@ -247,7 +330,8 @@
checkInvokeDirect(method.getReference(), invoke.asInvokeDirect());
}
MethodLookupResult lensLookup =
- graphLens.lookupMethod(invokedMethod, method.getReference(), invoke.getType());
+ graphLens.lookupMethod(
+ invokedMethod, method.getReference(), invoke.getType(), codeLens);
DexMethod actualTarget = lensLookup.getReference();
Invoke.Type actualInvokeType = lensLookup.getType();
@@ -313,15 +397,16 @@
@Override
public TypeElement getOutType() {
return graphLens
- .lookupType(invokedMethod.getReturnType())
+ .lookupType(invokedMethod.getReturnType(), codeLens)
.toTypeElement(appView);
}
};
+ prototypeChanges.verifyConstantReturnAccessibleInContext(
+ appView.withLiveness(), method, graphLens);
constantReturnMaterializingInstruction =
prototypeChanges.getConstantReturn(
appView.withLiveness(),
code,
- method,
invoke.getPosition(),
typeAndLocalInfo);
if (invoke.outValue().hasLocalInfo()) {
@@ -350,7 +435,7 @@
newOutValue = null;
}
} else {
- newOutValue = makeOutValue(invoke, code);
+ newOutValue = makeOutValue(invoke, code, graphLens, codeLens);
}
Map<SingleNumberValue, Map<DexType, Value>> parameterMap = new IdentityHashMap<>();
@@ -436,16 +521,10 @@
{
InstanceGet instanceGet = current.asInstanceGet();
DexField field = instanceGet.getField();
- FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ FieldLookupResult lookup = graphLens.lookupFieldResult(field, codeLens);
DexField rewrittenField = rewriteFieldReference(lookup, method);
- DexMethod replacementMethod =
- graphLens.lookupGetFieldForMethod(rewrittenField, method.getReference());
Value newOutValue = null;
- if (replacementMethod != null) {
- newOutValue = makeOutValue(instanceGet, code, rewrittenField);
- iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, newOutValue, instanceGet.inValues()));
- } else if (rewrittenField != field) {
+ if (rewrittenField != field) {
newOutValue = makeOutValue(instanceGet, code, rewrittenField);
iterator.replaceCurrentInstruction(
new InstanceGet(newOutValue, instanceGet.object(), rewrittenField));
@@ -478,20 +557,12 @@
{
InstancePut instancePut = current.asInstancePut();
DexField field = instancePut.getField();
- FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ FieldLookupResult lookup = graphLens.lookupFieldResult(field, codeLens);
iterator =
insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, instancePut, lookup);
DexField rewrittenField = rewriteFieldReference(lookup, method);
- DexMethod replacementMethod =
- graphLens.lookupPutFieldForMethod(rewrittenField, method.getReference());
- if (replacementMethod != null) {
- InvokeStatic replacement =
- new InvokeStatic(replacementMethod, null, instancePut.inValues());
- iterator.replaceCurrentInstruction(replacement);
- interfaceTypeToClassTypeRewriterHelper.insertCastsForOperandsIfNeeded(
- instancePut, replacement, blocks, block, iterator);
- } else if (rewrittenField != field) {
+ if (rewrittenField != field) {
Value rewrittenValue =
rewriteValueIfDefault(
code, iterator, field.type, rewrittenField.type, instancePut.value());
@@ -509,16 +580,10 @@
{
StaticGet staticGet = current.asStaticGet();
DexField field = staticGet.getField();
- FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ FieldLookupResult lookup = graphLens.lookupFieldResult(field, codeLens);
DexField rewrittenField = rewriteFieldReference(lookup, method);
- DexMethod replacementMethod =
- graphLens.lookupGetFieldForMethod(rewrittenField, method.getReference());
Value newOutValue = null;
- if (replacementMethod != null) {
- newOutValue = makeOutValue(staticGet, code, rewrittenField);
- iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, newOutValue, staticGet.inValues()));
- } else if (rewrittenField != field) {
+ if (rewrittenField != field) {
newOutValue = makeOutValue(staticGet, code, rewrittenField);
iterator.replaceCurrentInstruction(new StaticGet(newOutValue, rewrittenField));
}
@@ -550,21 +615,12 @@
{
StaticPut staticPut = current.asStaticPut();
DexField field = staticPut.getField();
- FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ FieldLookupResult lookup = graphLens.lookupFieldResult(field, codeLens);
iterator =
insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, staticPut, lookup);
DexField actualField = rewriteFieldReference(lookup, method);
- DexMethod replacementMethod =
- graphLens.lookupPutFieldForMethod(actualField, method.getReference());
-
- if (replacementMethod != null) {
- InvokeStatic replacement =
- new InvokeStatic(replacementMethod, staticPut.outValue(), staticPut.inValues());
- iterator.replaceCurrentInstruction(replacement);
- interfaceTypeToClassTypeRewriterHelper.insertCastsForOperandsIfNeeded(
- staticPut, replacement, blocks, block, iterator);
- } else if (actualField != field) {
+ if (actualField != field) {
Value rewrittenValue =
rewriteValueIfDefault(
code, iterator, field.type, actualField.type, staticPut.value());
@@ -583,7 +639,9 @@
.replaceInstructionIfTypeChanged(
checkCast.getType(),
(t, v) ->
- new CheckCast(v, checkCast.object(), t, checkCast.ignoreCompatRules()));
+ new CheckCast(v, checkCast.object(), t, checkCast.ignoreCompatRules()),
+ graphLens,
+ codeLens);
}
break;
@@ -592,7 +650,7 @@
ConstClass constClass = current.asConstClass();
new InstructionReplacer(code, current, iterator, affectedPhis)
.replaceInstructionIfTypeChanged(
- constClass.getValue(), (t, v) -> new ConstClass(v, t));
+ constClass.getValue(), (t, v) -> new ConstClass(v, t), graphLens, codeLens);
}
break;
@@ -601,7 +659,10 @@
InstanceOf instanceOf = current.asInstanceOf();
new InstructionReplacer(code, current, iterator, affectedPhis)
.replaceInstructionIfTypeChanged(
- instanceOf.type(), (t, v) -> new InstanceOf(v, instanceOf.value(), t));
+ instanceOf.type(),
+ (t, v) -> new InstanceOf(v, instanceOf.value(), t),
+ graphLens,
+ codeLens);
}
break;
@@ -611,7 +672,9 @@
new InstructionReplacer(code, current, iterator, affectedPhis)
.replaceInstructionIfTypeChanged(
multiNewArray.getArrayType(),
- (t, v) -> new InvokeMultiNewArray(t, v, multiNewArray.inValues()));
+ (t, v) -> new InvokeMultiNewArray(t, v, multiNewArray.inValues()),
+ graphLens,
+ codeLens);
}
break;
@@ -621,7 +684,9 @@
new InstructionReplacer(code, current, iterator, affectedPhis)
.replaceInstructionIfTypeChanged(
newArray.getArrayType(),
- (t, v) -> new InvokeNewArray(t, v, newArray.inValues()));
+ (t, v) -> new InvokeNewArray(t, v, newArray.inValues()),
+ graphLens,
+ codeLens);
}
break;
@@ -630,7 +695,10 @@
MoveException moveException = current.asMoveException();
new InstructionReplacer(code, current, iterator, affectedPhis)
.replaceInstructionIfTypeChanged(
- moveException.getExceptionType(), (t, v) -> new MoveException(v, t, options));
+ moveException.getExceptionType(),
+ (t, v) -> new MoveException(v, t, options),
+ graphLens,
+ codeLens);
}
break;
@@ -639,7 +707,10 @@
NewArrayEmpty newArrayEmpty = current.asNewArrayEmpty();
new InstructionReplacer(code, current, iterator, affectedPhis)
.replaceInstructionIfTypeChanged(
- newArrayEmpty.type, (t, v) -> new NewArrayEmpty(v, newArrayEmpty.size(), t));
+ newArrayEmpty.type,
+ (t, v) -> new NewArrayEmpty(v, newArrayEmpty.size(), t),
+ graphLens,
+ codeLens);
}
break;
@@ -647,7 +718,7 @@
{
DexType type = current.asNewInstance().clazz;
new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(type, NewInstance::new);
+ .replaceInstructionIfTypeChanged(type, NewInstance::new, graphLens, codeLens);
}
break;
@@ -661,7 +732,7 @@
break;
}
- insertCastForReturnIfNeeded(code, blocks, iterator, ret);
+ insertCastForReturnIfNeeded(code, blocks, iterator, ret, prototypeChangesForMethod);
DexType returnType = code.context().getReturnType();
Value retValue = ret.returnValue();
@@ -687,23 +758,6 @@
break;
case ARGUMENT:
- {
- Argument argument = current.asArgument();
- TypeElement currentArgumentType = argument.getOutType();
- TypeElement newArgumentType =
- method
- .getArgumentType(argument.getIndex())
- .toTypeElement(appView, currentArgumentType.nullability());
- if (!newArgumentType.equals(currentArgumentType)) {
- affectedPhis.addAll(argument.outValue().uniquePhiUsers());
- iterator.replaceCurrentInstruction(
- Argument.builder()
- .setIndex(argument.getIndex())
- .setFreshOutValue(code, newArgumentType)
- .setPosition(argument)
- .build());
- }
- }
break;
case ASSUME:
@@ -714,7 +768,7 @@
if (current.hasOutValue()) {
// For all other instructions, substitute any changed type.
TypeElement type = current.getOutType();
- TypeElement substituted = type.rewrittenWithLens(appView, graphLens);
+ TypeElement substituted = type.rewrittenWithLens(appView, graphLens, codeLens);
if (substituted != type) {
current.outValue().setType(substituted);
affectedPhis.addAll(current.outValue().uniquePhiUsers());
@@ -728,14 +782,173 @@
code.removeUnreachableBlocks();
}
if (!affectedPhis.isEmpty()) {
- new DestructivePhiTypeUpdater(appView).recomputeAndPropagateTypes(code, affectedPhis);
+ new DestructivePhiTypeUpdater(appView, graphLens, codeLens)
+ .recomputeAndPropagateTypes(code, affectedPhis);
}
+ code.removeAllDeadAndTrivialPhis();
+ removeUnusedArguments(method, code, unusedArguments);
// Finalize cast insertion.
interfaceTypeToClassTypeRewriterHelper.processWorklist();
assert code.isConsistentSSABeforeTypesAreCorrect();
- assert code.hasNoMergedClasses(appView);
+ }
+
+ // Applies the prototype changes of the current method to the argument instructions:
+ // - Replaces constant arguments by their constant value and then removes the (now unused)
+ // argument instruction
+ // - Removes unused arguments
+ // - Updates the type of arguments whose type has been strengthened
+ private void rewriteArguments(
+ IRCode code,
+ DexMethod originalMethodReference,
+ RewrittenPrototypeDescription prototypeChanges,
+ Set<Phi> affectedPhis,
+ Set<UnusedArgument> unusedArguments) {
+ List<Instruction> argumentPostlude = new ArrayList<>();
+ int oldArgumentIndex = 0;
+ int newArgumentIndex = 0;
+ InstructionListIterator instructionIterator = code.entryBlock().listIterator(code);
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ if (!instruction.isArgument()) {
+ break;
+ }
+
+ Argument argument = instruction.asArgument();
+ ArgumentInfo argumentInfo =
+ prototypeChanges.getArgumentInfoCollection().getArgumentInfo(oldArgumentIndex);
+ if (argumentInfo.isRemovedArgumentInfo()) {
+ rewriteRemovedArgument(
+ code,
+ instructionIterator,
+ originalMethodReference,
+ argument,
+ argumentInfo.asRemovedArgumentInfo(),
+ affectedPhis,
+ argumentPostlude,
+ unusedArguments);
+ } else {
+ if (argumentInfo.isRewrittenTypeInfo()) {
+ rewriteArgumentType(
+ code,
+ instructionIterator,
+ argument,
+ argumentInfo.asRewrittenTypeInfo(),
+ affectedPhis,
+ newArgumentIndex);
+ } else if (newArgumentIndex != oldArgumentIndex) {
+ instructionIterator.replaceCurrentInstruction(
+ Argument.builder()
+ .setIndex(newArgumentIndex)
+ .setFreshOutValue(code, argument.getOutType(), argument.getLocalInfo())
+ .setPosition(argument.getPosition())
+ .build());
+ }
+ newArgumentIndex++;
+ }
+ oldArgumentIndex++;
+ }
+
+ instructionIterator.previous();
+
+ if (!argumentPostlude.isEmpty()) {
+ for (Instruction instruction : argumentPostlude) {
+ instructionIterator.add(instruction);
+ }
+ }
+ }
+
+ private void rewriteRemovedArgument(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ DexMethod originalMethodReference,
+ Argument argument,
+ RemovedArgumentInfo removedArgumentInfo,
+ Set<Phi> affectedPhis,
+ List<Instruction> argumentPostlude,
+ Set<UnusedArgument> unusedArguments) {
+ Instruction replacement;
+ if (removedArgumentInfo.hasSingleValue()) {
+ SingleValue singleValue = removedArgumentInfo.getSingleValue();
+ TypeElement type =
+ removedArgumentInfo.getType().isReferenceType() && singleValue.isNull()
+ ? TypeElement.getNull()
+ : removedArgumentInfo.getType().toTypeElement(appView);
+ replacement =
+ singleValue.createMaterializingInstruction(
+ appView, code, TypeAndLocalInfoSupplier.create(type, argument.getLocalInfo()));
+ replacement.setPosition(
+ SourcePosition.builder().setLine(0).setMethod(originalMethodReference).build());
+ } else {
+ TypeElement unusedArgumentType = removedArgumentInfo.getType().toTypeElement(appView);
+ replacement = new UnusedArgument(code.createValue(unusedArgumentType));
+ replacement.setPosition(Position.none());
+ unusedArguments.add(replacement.asUnusedArgument());
+ }
+ argument.outValue().replaceUsers(replacement.outValue());
+ affectedPhis.addAll(replacement.outValue().uniquePhiUsers());
+ argumentPostlude.add(replacement);
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+
+ private void rewriteArgumentType(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ Argument argument,
+ RewrittenTypeInfo rewrittenTypeInfo,
+ Set<Phi> affectedPhis,
+ int newArgumentIndex) {
+ TypeElement rewrittenType = rewrittenTypeInfo.getNewType().toTypeElement(appView);
+ Argument replacement =
+ Argument.builder()
+ .setIndex(newArgumentIndex)
+ .setFreshOutValue(code, rewrittenType, argument.getLocalInfo())
+ .setPosition(argument.getPosition())
+ .build();
+ instructionIterator.replaceCurrentInstruction(replacement);
+ affectedPhis.addAll(replacement.outValue().uniquePhiUsers());
+ }
+
+ private void removeUnusedArguments(
+ ProgramMethod method, IRCode code, Set<UnusedArgument> unusedArguments) {
+ for (UnusedArgument unusedArgument : unusedArguments) {
+ if (unusedArgument.outValue().hasAnyUsers()) {
+ throw new Unreachable("Unused argument with users in " + method.toSourceString());
+ }
+ InstructionListIterator instructionIterator = unusedArgument.getBlock().listIterator(code);
+ instructionIterator.nextUntil(instruction -> instruction == unusedArgument);
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ }
+
+ private Deque<GraphLensInterval> getUnappliedLenses(ProgramMethod method) {
+ Deque<GraphLensInterval> unappliedLenses = new ArrayDeque<>(8);
+ GraphLens codeLens = appView.codeLens();
+ GraphLens currentLens = appView.graphLens();
+ DexMethod currentMethod = method.getReference();
+ while (currentLens != codeLens) {
+ assert currentLens.isNonIdentityLens();
+ NonIdentityGraphLens currentNonIdentityLens = currentLens.asNonIdentityLens();
+ NonIdentityGraphLens fromInclusiveLens = currentNonIdentityLens;
+ if (!currentNonIdentityLens.hasCustomCodeRewritings()) {
+ GraphLens fromInclusiveLensPredecessor = fromInclusiveLens.getPrevious();
+ while (fromInclusiveLensPredecessor.isNonIdentityLens()
+ && !fromInclusiveLensPredecessor.hasCustomCodeRewritings()
+ && fromInclusiveLensPredecessor != codeLens) {
+ fromInclusiveLens = fromInclusiveLensPredecessor.asNonIdentityLens();
+ fromInclusiveLensPredecessor = fromInclusiveLens.getPrevious();
+ }
+ }
+ GraphLensInterval unappliedLens =
+ new GraphLensInterval(
+ currentNonIdentityLens, fromInclusiveLens.getPrevious(), currentMethod);
+ unappliedLenses.addLast(unappliedLens);
+ currentLens = unappliedLens.getCodeLens();
+ currentMethod = currentNonIdentityLens.getOriginalMethodSignature(currentMethod, currentLens);
+ }
+ assert unappliedLenses.size() <= 8;
+ return unappliedLenses;
}
private InstructionListIterator insertCastForFieldAssignmentIfNeeded(
@@ -778,10 +991,19 @@
InvokeMethod invoke,
MethodLookupResult lookup) {
// If the invoke has been staticized, then synthesize a null check for the receiver.
- if (!invoke.isInvokeMethodWithReceiver() || !lookup.getType().isStatic()) {
+ if (!invoke.isInvokeMethodWithReceiver()) {
return iterator;
}
+ ArgumentInfo receiverArgumentInfo =
+ lookup.getPrototypeChanges().getArgumentInfoCollection().getArgumentInfo(0);
+ if (!receiverArgumentInfo.isRemovedArgumentInfo()
+ || !receiverArgumentInfo.asRemovedArgumentInfo().isCheckNullOrZeroSet()) {
+ return iterator;
+ }
+
+ assert lookup.getType().isStatic();
+
TypeElement receiverType = invoke.asInvokeMethodWithReceiver().getReceiver().getType();
if (receiverType.isDefinitelyNotNull()) {
return iterator;
@@ -798,15 +1020,6 @@
iterator.insertNullCheckInstruction(
appView, code, blocks, invoke.getFirstArgument(), nullCheckPosition);
- // TODO(b/199864962): Lens code rewriting should follow the order of graph lenses, i.e., enum
- // unboxing rewriting should happen after method staticizing.
- if (receiverType.isClassType()
- && appView.unboxedEnums().isUnboxedEnum(receiverType.asClassType().getClassType())) {
- iterator.previousUntil(instruction -> instruction == nullCheck);
- iterator.next();
- enumUnboxer.rewriteNullCheck(iterator, nullCheck);
- }
-
// Reset the block iterator.
if (invoke.getBlock().hasCatchHandlers()) {
BasicBlock splitBlock = invoke.getBlock();
@@ -871,11 +1084,11 @@
}
private InstructionListIterator insertCastForReturnIfNeeded(
- IRCode code, BasicBlockIterator blocks, InstructionListIterator iterator, Return ret) {
- RewrittenPrototypeDescription prototypeChanges =
- appView
- .graphLens()
- .lookupPrototypeChangesForMethodDefinition(code.context().getReference());
+ IRCode code,
+ BasicBlockIterator blocks,
+ InstructionListIterator iterator,
+ Return ret,
+ RewrittenPrototypeDescription prototypeChanges) {
if (!prototypeChanges.hasRewrittenReturnInfo()
|| !prototypeChanges.getRewrittenReturnInfo().hasCastType()) {
return iterator;
@@ -982,7 +1195,7 @@
return;
}
DexMethod invokedMethod = invoke.getInvokedMethod();
- if (invokedMethod.name != appView.dexItemFactory().constructorMethodName) {
+ if (invokedMethod.name != factory.constructorMethodName) {
// Not a constructor call.
return;
}
@@ -1020,7 +1233,8 @@
*
* @return true if any dead catch handlers were removed.
*/
- private boolean unlinkDeadCatchHandlers(BasicBlock block) {
+ private boolean unlinkDeadCatchHandlers(
+ BasicBlock block, NonIdentityGraphLens graphLens, GraphLens codeLens) {
assert block.hasCatchHandlers();
CatchHandlers<BasicBlock> catchHandlers = block.getCatchHandlers();
List<DexType> guards = catchHandlers.getGuards();
@@ -1030,7 +1244,7 @@
List<BasicBlock> deadCatchHandlers = new ArrayList<>();
for (int i = 0; i < guards.size(); i++) {
// The type may have changed due to class merging.
- DexType guard = appView.graphLens().lookupType(guards.get(i));
+ DexType guard = graphLens.lookupType(guards.get(i), codeLens);
boolean guardSeenBefore = !previouslySeenGuards.add(guard);
if (guardSeenBefore) {
deadCatchHandlers.add(targets.get(i));
@@ -1060,10 +1274,13 @@
}
void replaceInstructionIfTypeChanged(
- DexType type, BiFunction<DexType, Value, Instruction> constructor) {
- DexType newType = appView.graphLens().lookupType(type);
+ DexType type,
+ BiFunction<DexType, Value, Instruction> constructor,
+ NonIdentityGraphLens graphLens,
+ GraphLens codeLens) {
+ DexType newType = graphLens.lookupType(type, codeLens);
if (newType != type) {
- Value newOutValue = makeOutValue(current, code);
+ Value newOutValue = makeOutValue(current, code, graphLens, codeLens);
Instruction newInstruction = constructor.apply(newType, newOutValue);
iterator.replaceCurrentInstruction(newInstruction);
if (newOutValue != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index c411187..0702fc4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -38,6 +38,7 @@
private final DexDefinitionSupplier definitions;
private final GraphLens graphLens;
+ private final GraphLens codeLens;
private final Map<DexProto, DexProto> protoFixupCache = new ConcurrentHashMap<>();
@@ -46,18 +47,21 @@
private final Map<DexCallSite, DexCallSite> rewrittenCallSiteCache;
public LensCodeRewriterUtils(AppView<?> appView) {
- this(appView, appView.graphLens());
+ this(appView, appView.graphLens(), appView.codeLens());
}
public LensCodeRewriterUtils(AppView<?> appView, boolean enableCallSiteCaching) {
this.definitions = appView;
this.graphLens = appView.graphLens();
+ this.codeLens = appView.codeLens();
this.rewrittenCallSiteCache = enableCallSiteCaching ? new ConcurrentHashMap<>() : null;
}
- public LensCodeRewriterUtils(DexDefinitionSupplier definitions, GraphLens graphLens) {
+ public LensCodeRewriterUtils(
+ DexDefinitionSupplier definitions, GraphLens graphLens, GraphLens codeLens) {
this.definitions = definitions;
this.graphLens = graphLens;
+ this.codeLens = codeLens;
this.rewrittenCallSiteCache = null;
}
@@ -108,7 +112,7 @@
LambdaDescriptor.getMainFunctionalInterfaceMethodReference(
callSite, definitions.dexItemFactory());
return graphLens
- .lookupMethod(method, context.getReference(), Invoke.Type.INTERFACE)
+ .lookupMethod(method, context.getReference(), Invoke.Type.INTERFACE, codeLens)
.getReference()
.getName();
}
@@ -119,7 +123,8 @@
DexMethod invokedMethod = methodHandle.asMethod();
MethodHandleType oldType = methodHandle.type;
MethodLookupResult lensLookup =
- graphLens.lookupMethod(invokedMethod, context.getReference(), oldType.toInvokeType());
+ graphLens.lookupMethod(
+ invokedMethod, context.getReference(), oldType.toInvokeType(), codeLens);
DexMethod rewrittenTarget = lensLookup.getReference();
DexMethod actualTarget;
MethodHandleType newType;
@@ -139,7 +144,7 @@
definitions
.dexItemFactory()
.createMethod(
- graphLens.lookupType(invokedMethod.holder),
+ graphLens.lookupType(invokedMethod.holder, codeLens),
rewrittenTarget.proto,
rewrittenTarget.name);
newType = oldType;
@@ -166,7 +171,7 @@
}
} else {
DexField field = methodHandle.asField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = graphLens.lookupField(field, codeLens);
if (actualField != field) {
return definitions
.dexItemFactory()
@@ -211,7 +216,7 @@
return rewriteDexMethodType(value.asDexValueMethodType());
case TYPE:
DexType oldType = value.asDexValueType().value;
- DexType newType = graphLens.lookupType(oldType);
+ DexType newType = graphLens.lookupType(oldType, codeLens);
return newType != oldType ? new DexValueType(newType) : value;
default:
return value;
@@ -221,7 +226,8 @@
public DexProto rewriteProto(DexProto proto) {
return definitions
.dexItemFactory()
- .applyClassMappingToProto(proto, graphLens::lookupType, protoFixupCache);
+ .applyClassMappingToProto(
+ proto, type -> graphLens.lookupType(type, codeLens), protoFixupCache);
}
private DexValueMethodHandle rewriteDexValueMethodHandle(
@@ -231,7 +237,7 @@
return newHandle != oldHandle ? new DexValueMethodHandle(newHandle) : methodHandle;
}
- public boolean hasGraphLens(GraphLens graphLens) {
- return this.graphLens == graphLens;
+ public boolean hasGraphLens(GraphLens graphLens, GraphLens codeLens) {
+ return this.graphLens == graphLens && this.codeLens == codeLens;
}
}
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 ee7f8e1..49ad698 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
@@ -100,8 +100,7 @@
public static List<DexMethod> generateListOfBackportedMethods(
AndroidApp androidApp, InternalOptions options, ExecutorService executor) throws IOException {
List<DexMethod> methods = new ArrayList<>();
- PrefixRewritingMapper rewritePrefix =
- options.desugaredLibrarySpecification.getPrefixRewritingMapper();
+ PrefixRewritingMapper rewritePrefix = options.getPrefixRewritingMapper();
AppInfo appInfo = null;
if (androidApp != null) {
DexApplication app =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
index eb4f662..bf96b67 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -11,9 +11,10 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Map;
import java.util.Set;
@@ -30,6 +31,8 @@
public abstract DexType rewrittenType(DexType type, AppView<?> appView);
+ public abstract DexType rewrittenContextType(DexType type, AppView<?> appView);
+
public boolean hasRewrittenType(DexType type, AppView<?> appView) {
return rewrittenType(type, appView) != null;
}
@@ -48,8 +51,6 @@
public abstract boolean isRewriting();
- public abstract boolean shouldRewriteTypeName(String typeName);
-
public abstract void forAllRewrittenTypes(Consumer<DexType> consumer);
public static class DesugarPrefixRewritingMapper extends PrefixRewritingMapper {
@@ -57,7 +58,6 @@
private final Set<DexType> notRewritten = Sets.newConcurrentHashSet();
private final Map<DexType, DexType> rewritten = new ConcurrentHashMap<>();
private final Map<DexString, DexString> initialPrefixes;
- private final Set<String> initialPrefixStrings;
private final DexItemFactory factory;
private final boolean l8Compilation;
@@ -67,13 +67,10 @@
this.factory = itemFactory;
this.l8Compilation = libraryCompilation;
ImmutableMap.Builder<DexString, DexString> builder = ImmutableMap.builder();
- ImmutableSet.Builder<String> prefixStringBuilder = ImmutableSet.builder();
for (String key : prefixes.keySet()) {
- prefixStringBuilder.add(key);
builder.put(toDescriptorPrefix(key), toDescriptorPrefix(prefixes.get(key)));
}
this.initialPrefixes = builder.build();
- this.initialPrefixStrings = prefixStringBuilder.build();
validatePrefixes(prefixes);
}
@@ -129,6 +126,36 @@
return computePrefix(type, appView);
}
+ @Override
+ public DexType rewrittenContextType(DexType type, AppView<?> appView) {
+ DexType rewritten = rewrittenType(type, appView);
+ if (rewritten != null) {
+ return rewritten;
+ }
+ LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
+ appView.options().desugaredLibrarySpecification;
+ if (desugaredLibrarySpecification.getEmulateLibraryInterface().containsKey(type)) {
+ return desugaredLibrarySpecification.getEmulateLibraryInterface().get(type);
+ }
+ for (Map<DexType, DexType> value :
+ desugaredLibrarySpecification.getRetargetCoreLibMember().values()) {
+ if (value.containsKey(type)) {
+ // Hack until machine specification are ready.
+ String prefix =
+ DescriptorUtils.getJavaTypeFromBinaryName(
+ desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix());
+ String interfaceType = type.toString();
+ int firstPackage = interfaceType.indexOf('.');
+ return appView
+ .dexItemFactory()
+ .createType(
+ DescriptorUtils.javaTypeToDescriptor(
+ prefix + interfaceType.substring(firstPackage + 1)));
+ }
+ }
+ return null;
+ }
+
// Besides L8 compilation, program types should not be rewritten.
private void failIfRewritingProgramType(DexType type, AppView<?> appView) {
if (l8Compilation) {
@@ -192,16 +219,54 @@
public boolean isRewriting() {
return true;
}
+ }
+
+ public static class MachineDesugarPrefixRewritingMapper extends PrefixRewritingMapper {
+
+ private final PrefixRewritingMapper mapper;
+ private final Map<DexType, DexType> rewriteType;
+ private final Map<DexType, DexType> rewriteDerivedTypeOnly;
+
+ public MachineDesugarPrefixRewritingMapper(
+ PrefixRewritingMapper mapper, MachineRewritingFlags flags) {
+ this.mapper = mapper;
+ this.rewriteType = new ConcurrentHashMap<>(flags.getRewriteType());
+ rewriteDerivedTypeOnly = flags.getRewriteDerivedTypeOnly();
+ }
@Override
- public boolean shouldRewriteTypeName(String typeName) {
- // TODO(b/154800164): We could use tries instead of looking-up everywhere.
- for (DexString prefix : initialPrefixes.keySet()) {
- if (typeName.startsWith(prefix.toString())) {
- return true;
- }
+ public DexType rewrittenType(DexType type, AppView<?> appView) {
+ assert mapper.rewrittenType(type, appView) == rewriteType.get(type);
+ return rewriteType.get(type);
+ }
+
+ @Override
+ public DexType rewrittenContextType(DexType context, AppView<?> appView) {
+ if (rewriteType.containsKey(context)) {
+ return rewriteType.get(context);
}
- return false;
+ return rewriteDerivedTypeOnly.get(context);
+ }
+
+ @Override
+ public void rewriteType(DexType type, DexType rewrittenType) {
+ mapper.rewriteType(type, rewrittenType);
+ rewriteType.compute(
+ type,
+ (t, val) -> {
+ assert val == null || val == rewrittenType;
+ return rewrittenType;
+ });
+ }
+
+ @Override
+ public boolean isRewriting() {
+ return true;
+ }
+
+ @Override
+ public void forAllRewrittenTypes(Consumer<DexType> consumer) {
+ rewriteType.keySet().forEach(consumer);
}
}
@@ -213,6 +278,11 @@
}
@Override
+ public DexType rewrittenContextType(DexType type, AppView<?> appView) {
+ return null;
+ }
+
+ @Override
public void rewriteType(DexType type, DexType rewrittenType) {}
@Override
@@ -221,11 +291,6 @@
}
@Override
- public boolean shouldRewriteTypeName(String typeName) {
- return false;
- }
-
- @Override
public void forAllRewrittenTypes(Consumer<DexType> consumer) {}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
index 0795a4e..150e6c4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
@@ -201,8 +201,7 @@
clazz.isInterface(),
eventConsumer)
.generateCfCode();
- DexEncodedMethod newMethod =
- wrapperSynthesizor.newSynthesizedMethod(methodToInstall, originalMethod, cfCode);
+ DexEncodedMethod newMethod = wrapperSynthesizor.newSynthesizedMethod(methodToInstall, cfCode);
newMethod.setCode(cfCode, appView);
if (originalMethod.isLibraryMethodOverride().isTrue()) {
newMethod.setLibraryMethodOverride(OptionalBool.TRUE);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
index 558faf7..987dc8d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -235,6 +235,7 @@
.dexItemFactory()
.createSynthesizedType(
DescriptorUtils.javaTypeToDescriptor(VIVIFIED_PREFIX + type.toString()));
+ // We would need to ensure a classpath class for each type to remove this rewriteType call.
appView.rewritePrefix.rewriteType(vivifiedType, type);
return vivifiedType;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
index 55d61b9..4a8cabe 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -29,7 +29,6 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterConstructorCfCodeProvider;
-import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterThrowRuntimeExceptionCfCodeProvider;
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterVivifiedWrapperCfCodeProvider;
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperConversionCfCodeProvider;
@@ -41,14 +40,11 @@
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@@ -387,22 +383,8 @@
wrapperField, reverseWrapperField, context)));
}
- private boolean isInvalidWrapper(DexClass clazz) {
- return Iterables.any(allImplementedMethods(clazz), DexEncodedMethod::isFinal);
- }
-
private CfCode computeProgramConversionMethodCode(
DexField wrapperField, DexField reverseWrapperField, DexClass context) {
- if (isInvalidWrapper(context)) {
- return new APIConverterThrowRuntimeExceptionCfCodeProvider(
- appView,
- factory.createString(
- "Unsupported conversion for "
- + context.type
- + ". See compilation time warnings for more details."),
- wrapperField.holder)
- .generateCfCode();
- }
assert context.isProgramClass();
return new APIConverterWrapperConversionCfCodeProvider(
appView, reverseWrapperField, wrapperField)
@@ -459,7 +441,7 @@
private Collection<DexEncodedMethod> synthesizeVirtualMethodsForVivifiedTypeWrapper(
DexClass dexClass, DexEncodedField wrapperField) {
- List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
+ Iterable<DexMethod> allImplementedMethods = allImplementedMethods(dexClass);
List<DexEncodedMethod> generatedMethods = new ArrayList<>();
// Each method should use only types in their signature, but each method the wrapper forwards
// to should used only vivified types.
@@ -470,30 +452,23 @@
// v2 <- convertTypeToVivifiedType(v0);
// v3 <- wrappedValue.foo(v2,v1);
// return v3;
- Set<DexMethod> finalMethods = Sets.newIdentityHashSet();
- for (DexEncodedMethod dexEncodedMethod : dexMethods) {
- DexClass holderClass = appView.definitionFor(dexEncodedMethod.getHolderType());
+ for (DexMethod method : allImplementedMethods) {
+ DexClass holderClass = appView.definitionFor(method.getHolderType());
boolean isInterface;
if (holderClass == null) {
assert appView
.options()
.desugaredLibrarySpecification
.getEmulateLibraryInterface()
- .containsValue(dexEncodedMethod.getHolderType());
+ .containsValue(method.getHolderType());
isInterface = true;
} else {
isInterface = holderClass.isInterface();
}
DexMethod methodToInstall =
- factory.createMethod(
- wrapperField.getHolderType(),
- dexEncodedMethod.getReference().proto,
- dexEncodedMethod.getReference().name);
+ factory.createMethod(wrapperField.getHolderType(), method.proto, method.name);
CfCode cfCode;
- if (dexEncodedMethod.isFinal()) {
- finalMethods.add(dexEncodedMethod.getReference());
- continue;
- } else if (dexClass.isProgramClass()) {
+ if (dexClass.isProgramClass()) {
cfCode =
new APIConverterVivifiedWrapperCfCodeProvider(
appView, methodToInstall, wrapperField.getReference(), this, isInterface)
@@ -501,16 +476,15 @@
} else {
cfCode = null;
}
- DexEncodedMethod newDexEncodedMethod =
- newSynthesizedMethod(methodToInstall, dexEncodedMethod, cfCode);
+ DexEncodedMethod newDexEncodedMethod = newSynthesizedMethod(methodToInstall, cfCode);
generatedMethods.add(newDexEncodedMethod);
}
- return finalizeWrapperMethods(generatedMethods, finalMethods);
+ return generatedMethods;
}
private Collection<DexEncodedMethod> synthesizeVirtualMethodsForTypeWrapper(
DexClass dexClass, DexEncodedField wrapperField) {
- List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
+ Iterable<DexMethod> dexMethods = allImplementedMethods(dexClass);
List<DexEncodedMethod> generatedMethods = new ArrayList<>();
// Each method should use only vivified types in their signature, but each method the wrapper
// forwards
@@ -522,87 +496,64 @@
// v2 <- convertVivifiedTypeToType(v0);
// v3 <- wrappedValue.foo(v2,v1);
// return v3;
- Set<DexMethod> finalMethods = Sets.newIdentityHashSet();
- for (DexEncodedMethod dexEncodedMethod : dexMethods) {
- DexClass holderClass = appView.definitionFor(dexEncodedMethod.getHolderType());
+ for (DexMethod method : dexMethods) {
+ DexClass holderClass = appView.definitionFor(method.getHolderType());
assert holderClass != null || appView.options().isDesugaredLibraryCompilation();
boolean isInterface = holderClass == null || holderClass.isInterface();
DexMethod methodToInstall =
DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
- dexEncodedMethod.getReference(), wrapperField.getHolderType(), appView);
+ method, wrapperField.getHolderType(), appView);
CfCode cfCode;
- if (dexEncodedMethod.isFinal()) {
- finalMethods.add(dexEncodedMethod.getReference());
- continue;
- } else if (dexClass.isProgramClass()) {
+ if (dexClass.isProgramClass()) {
cfCode =
new APIConverterWrapperCfCodeProvider(
- appView,
- dexEncodedMethod.getReference(),
- wrapperField.getReference(),
- this,
- isInterface)
+ appView, method, wrapperField.getReference(), this, isInterface)
.generateCfCode();
} else {
cfCode = null;
}
- DexEncodedMethod newDexEncodedMethod =
- newSynthesizedMethod(methodToInstall, dexEncodedMethod, cfCode);
+ DexEncodedMethod newDexEncodedMethod = newSynthesizedMethod(methodToInstall, cfCode);
generatedMethods.add(newDexEncodedMethod);
}
- return finalizeWrapperMethods(generatedMethods, finalMethods);
+ return generatedMethods;
}
- private Collection<DexEncodedMethod> finalizeWrapperMethods(
- List<DexEncodedMethod> generatedMethods, Set<DexMethod> finalMethods) {
- if (finalMethods.isEmpty()) {
- return generatedMethods;
- }
- // Wrapper is invalid, no need to add the virtual methods.
- reportFinalMethodsInWrapper(finalMethods);
- return Collections.emptyList();
- }
-
- private void reportFinalMethodsInWrapper(Set<DexMethod> methods) {
- String[] methodArray =
- methods.stream().map(method -> method.holder + "#" + method.name).toArray(String[]::new);
- appView
- .options()
- .reporter
- .warning(
- new StringDiagnostic(
- "Desugared library API conversion: cannot wrap final methods "
- + Arrays.toString(methodArray)
- + ". "
- + methods.iterator().next().holder
- + " is marked as invalid and will throw a runtime exception upon conversion."));
- }
-
- DexEncodedMethod newSynthesizedMethod(
- DexMethod methodToInstall, DexEncodedMethod template, Code code) {
- MethodAccessFlags newFlags = template.accessFlags.copy();
- assert newFlags.isPublic();
- // It can happen that we wrap an abstract method, in which case the wrapping method is no
- // longer abstract.
- if (code != null) {
- newFlags.unsetAbstract();
- }
- // TODO(b/146114533): Fix inlining in synthetic methods and remove unsetBridge.
- newFlags.unsetBridge();
- newFlags.setSynthetic();
+ DexEncodedMethod newSynthesizedMethod(DexMethod methodToInstall, Code code) {
+ MethodAccessFlags newFlags =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false);
+ ComputedApiLevel apiLevelForDefinition =
+ appView.enableWholeProgramOptimizations()
+ ? ComputedApiLevel.notSet()
+ : appView
+ .apiLevelCompute()
+ .computeApiLevelForDefinition(methodToInstall, factory, ComputedApiLevel.unknown());
+ // Since the method is a forwarding method, the api level for code is the same as the
+ // definition.
+ ComputedApiLevel apiLevelForCode = apiLevelForDefinition;
return DexEncodedMethod.syntheticBuilder()
.setMethod(methodToInstall)
.setAccessFlags(newFlags)
.setCode(code)
- .setApiLevelForDefinition(template.getApiLevelForDefinition())
- .setApiLevelForCode(
- code == null ? ComputedApiLevel.notSet() : template.getApiLevelForCode())
+ .setApiLevelForDefinition(apiLevelForDefinition)
+ .setApiLevelForCode(code == null ? ComputedApiLevel.notSet() : apiLevelForCode)
.build();
}
- private List<DexEncodedMethod> allImplementedMethods(DexClass clazz) {
- return allImplementedMethodsCache.computeIfAbsent(
- clazz.type, type -> internalAllImplementedMethods(clazz));
+ private Iterable<DexMethod> allImplementedMethods(DexClass clazz) {
+ if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
+ return appView
+ .options()
+ .testing
+ .machineDesugaredLibrarySpecification
+ .getRewritingFlags()
+ .getWrappers()
+ .get(clazz.type);
+ }
+ List<DexEncodedMethod> dexEncodedMethods =
+ allImplementedMethodsCache.computeIfAbsent(
+ clazz.type, type -> internalAllImplementedMethods(clazz));
+ return Iterables.transform(dexEncodedMethods, m -> m.getReference());
}
private List<DexEncodedMethod> internalAllImplementedMethods(DexClass libraryClass) {
@@ -641,6 +592,7 @@
workList.add(superClass);
}
}
+ assert !Iterables.any(implementedMethods, DexEncodedMethod::isFinal);
return implementedMethods;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
index c5154cd..6eb2424 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
@@ -37,7 +37,6 @@
* method is the method on the companion class.
*/
private final DerivedMethod interfaceMethod;
-
private final DerivedMethod emulatedDispatchMethod;
private final DerivedMethod forwardingMethod;
private final LinkedHashMap<DexType, DerivedMethod> dispatchCases;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
new file mode 100644
index 0000000..0f9a520
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2022, 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.desugar.desugaredlibrary.machinespecification;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import java.util.Map;
+
+public class EmulatedInterfaceDescriptor {
+ private final DexType rewrittenType;
+ private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods;
+
+ public EmulatedInterfaceDescriptor(
+ DexType rewrittenType, Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods) {
+ this.rewrittenType = rewrittenType;
+ this.emulatedMethods = emulatedMethods;
+ }
+
+ public DexType getRewrittenType() {
+ return rewrittenType;
+ }
+
+ public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedMethods() {
+ return emulatedMethods;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index 2a88f9a..501482e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -4,15 +4,18 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
-/** TODO(b/184026720): Move the rest of the flags. */
public class MachineDesugaredLibrarySpecification {
private final boolean libraryCompilation;
+ private final MachineTopLevelFlags topLevelFlags;
private final MachineRewritingFlags rewritingFlags;
public MachineDesugaredLibrarySpecification(
- boolean libraryCompilation, MachineRewritingFlags rewritingFlags) {
+ boolean libraryCompilation,
+ MachineTopLevelFlags topLevelFlags,
+ MachineRewritingFlags rewritingFlags) {
this.libraryCompilation = libraryCompilation;
+ this.topLevelFlags = topLevelFlags;
this.rewritingFlags = rewritingFlags;
}
@@ -20,6 +23,10 @@
return libraryCompilation;
}
+ public MachineTopLevelFlags getTopLevelFlags() {
+ return topLevelFlags;
+ }
+
public MachineRewritingFlags getRewritingFlags() {
return rewritingFlags;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index f73e1c9..a6c487e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -5,8 +5,16 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
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.utils.Pair;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
public class MachineRewritingFlags {
@@ -15,14 +23,33 @@
}
MachineRewritingFlags(
+ Map<DexType, DexType> rewriteType,
+ Map<DexType, DexType> rewriteDerivedTypeOnly,
Map<DexMethod, DexMethod> staticRetarget,
Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget,
- Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget) {
+ Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget,
+ Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces,
+ Map<DexType, List<DexMethod>> wrappers,
+ Map<DexType, DexType> legacyBackport,
+ Set<DexType> dontRetarget,
+ Map<DexType, Pair<DexType, DexString>> customConversions) {
+ this.rewriteType = rewriteType;
+ this.rewriteDerivedTypeOnly = rewriteDerivedTypeOnly;
this.staticRetarget = staticRetarget;
this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget;
this.emulatedVirtualRetarget = emulatedVirtualRetarget;
+ this.emulatedInterfaces = emulatedInterfaces;
+ this.wrappers = wrappers;
+ this.legacyBackport = legacyBackport;
+ this.dontRetarget = dontRetarget;
+ this.customConversions = customConversions;
}
+ // Rewrites all the references to the keys as well as synthetic types derived from any key.
+ private final Map<DexType, DexType> rewriteType;
+ // Rewrites only synthetic types derived from any key.
+ private final Map<DexType, DexType> rewriteDerivedTypeOnly;
+
// Static methods to retarget, duplicated to library boundaries.
private final Map<DexMethod, DexMethod> staticRetarget;
@@ -37,6 +64,24 @@
// Virtual methods to retarget through emulated dispatch.
private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
+ // Emulated interface descriptors.
+ private final Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces;
+
+ // Wrappers and the list of methods they implement.
+ private final Map<DexType, List<DexMethod>> wrappers;
+
+ private final Map<DexType, DexType> legacyBackport;
+ private final Set<DexType> dontRetarget;
+ private final Map<DexType, Pair<DexType, DexString>> customConversions;
+
+ public Map<DexType, DexType> getRewriteType() {
+ return rewriteType;
+ }
+
+ public Map<DexType, DexType> getRewriteDerivedTypeOnly() {
+ return rewriteDerivedTypeOnly;
+ }
+
public Map<DexMethod, DexMethod> getStaticRetarget() {
return staticRetarget;
}
@@ -49,16 +94,55 @@
return emulatedVirtualRetarget;
}
+ public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
+ return emulatedInterfaces;
+ }
+
+ public Map<DexType, List<DexMethod>> getWrappers() {
+ return wrappers;
+ }
+
+ public Map<DexType, DexType> getLegacyBackport() {
+ return legacyBackport;
+ }
+
+ public Set<DexType> getDontRetarget() {
+ return dontRetarget;
+ }
+
+ public Map<DexType, Pair<DexType, DexString>> getCustomConversions() {
+ return customConversions;
+ }
+
public static class Builder {
Builder() {}
+ private final Map<DexType, DexType> rewriteType = new IdentityHashMap<>();
+ private final Map<DexType, DexType> rewriteDerivedTypeOnly = new IdentityHashMap<>();
private final ImmutableMap.Builder<DexMethod, DexMethod> staticRetarget =
ImmutableMap.builder();
private final ImmutableMap.Builder<DexMethod, DexMethod> nonEmulatedVirtualRetarget =
ImmutableMap.builder();
private final ImmutableMap.Builder<DexMethod, EmulatedDispatchMethodDescriptor>
emulatedVirtualRetarget = ImmutableMap.builder();
+ private final ImmutableMap.Builder<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces =
+ ImmutableMap.builder();
+ private final ImmutableMap.Builder<DexType, List<DexMethod>> wrappers = ImmutableMap.builder();
+ private final ImmutableMap.Builder<DexType, DexType> legacyBackport = ImmutableMap.builder();
+ private final ImmutableSet.Builder<DexType> dontRetarget = ImmutableSet.builder();
+ private final ImmutableMap.Builder<DexType, Pair<DexType, DexString>> customConversions =
+ ImmutableMap.builder();
+
+ public void rewriteType(DexType src, DexType target) {
+ assert src != null;
+ assert target != null;
+ rewriteType.put(src, target);
+ }
+
+ public void rewriteDerivedTypeOnly(DexType src, DexType target) {
+ rewriteDerivedTypeOnly.put(src, target);
+ }
public void putStaticRetarget(DexMethod src, DexMethod dest) {
staticRetarget.put(src, dest);
@@ -68,15 +152,42 @@
nonEmulatedVirtualRetarget.put(src, dest);
}
+ public void putEmulatedInterface(DexType src, EmulatedInterfaceDescriptor descriptor) {
+ emulatedInterfaces.put(src, descriptor);
+ }
+
public void putEmulatedVirtualRetarget(DexMethod src, EmulatedDispatchMethodDescriptor dest) {
emulatedVirtualRetarget.put(src, dest);
}
+ public void addWrapper(DexType wrapperConversion, List<DexMethod> methods) {
+ wrappers.put(wrapperConversion, ImmutableList.copyOf(methods));
+ }
+
+ public void putLegacyBackport(DexType src, DexType target) {
+ legacyBackport.put(src, target);
+ }
+
+ public void addDontRetarget(DexType type) {
+ dontRetarget.add(type);
+ }
+
+ public void putCustomConversion(DexType src, DexType conversionType, DexString conversionName) {
+ customConversions.put(src, new Pair<>(conversionType, conversionName));
+ }
+
public MachineRewritingFlags build() {
return new MachineRewritingFlags(
+ rewriteType,
+ rewriteDerivedTypeOnly,
staticRetarget.build(),
nonEmulatedVirtualRetarget.build(),
- emulatedVirtualRetarget.build());
+ emulatedVirtualRetarget.build(),
+ emulatedInterfaces.build(),
+ wrappers.build(),
+ legacyBackport.build(),
+ dontRetarget.build(),
+ customConversions.build());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
new file mode 100644
index 0000000..f426219
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2022, 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.desugar.desugaredlibrary.machinespecification;
+
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.List;
+
+public class MachineTopLevelFlags {
+
+ private final AndroidApiLevel requiredCompilationAPILevel;
+ private final String synthesizedLibraryClassesPackagePrefix;
+ private final String identifier;
+ private final String jsonSource;
+ // Setting supportAllCallbacksFromLibrary reduces the number of generated call-backs,
+ // more specifically:
+ // - no call-back is generated for emulated interface method overrides (forEach, etc.)
+ // - no call-back is generated inside the desugared library itself.
+ // Such setting decreases significantly the desugared library dex file, but virtual calls from
+ // within the library to desugared library classes instances as receiver may be incorrect, for
+ // example the method forEach in Iterable may be executed over a concrete implementation.
+ private final boolean supportAllCallbacksFromLibrary;
+ private final List<String> extraKeepRules;
+
+ public MachineTopLevelFlags(
+ AndroidApiLevel requiredCompilationAPILevel,
+ String synthesizedLibraryClassesPackagePrefix,
+ String identifier,
+ String jsonSource,
+ boolean supportAllCallbacksFromLibrary,
+ List<String> extraKeepRules) {
+ this.requiredCompilationAPILevel = requiredCompilationAPILevel;
+ this.synthesizedLibraryClassesPackagePrefix = synthesizedLibraryClassesPackagePrefix;
+ this.identifier = identifier;
+ this.jsonSource = jsonSource;
+ this.supportAllCallbacksFromLibrary = supportAllCallbacksFromLibrary;
+ this.extraKeepRules = extraKeepRules;
+ }
+
+ public AndroidApiLevel getRequiredCompilationAPILevel() {
+ return requiredCompilationAPILevel;
+ }
+
+ public String getSynthesizedLibraryClassesPackagePrefix() {
+ return synthesizedLibraryClassesPackagePrefix;
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public String getJsonSource() {
+ return jsonSource;
+ }
+
+ public boolean isSupportAllCallbacksFromLibrary() {
+ return supportAllCallbacksFromLibrary;
+ }
+
+ public List<String> getExtraKeepRules() {
+ return extraKeepRules;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index 7c5d1c3..a54cd8d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -139,13 +139,15 @@
AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
MethodResolutionResult resolutionResult =
appInfo.resolveMethod(invokedMethod, cfInvoke.isInterface());
- if (!resolutionResult.isSingleResolution()) {
- return NO_REWRITING;
- }
- DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
+ // We are required to use the invokedMethod if it does not resolve due to the rewriting of
+ // private methods absent from the library.
+ DexMethod singleTarget =
+ resolutionResult.isSingleResolution()
+ ? resolutionResult.getSingleTarget().getReference()
+ : invokedMethod;
assert singleTarget != null;
if (cfInvoke.isInvokeStatic()) {
- DexMethod retarget = staticRetarget.get(singleTarget.getReference());
+ DexMethod retarget = staticRetarget.get(singleTarget);
return retarget == null
? NO_REWRITING
: InvokeRetargetingResult.createInvokeRetargetingResult(retarget);
@@ -163,10 +165,8 @@
return retarget;
}
- private InvokeRetargetingResult computeNonStaticRetarget(DexEncodedMethod singleTarget) {
- assert !singleTarget.isStatic();
- DexMethod reference = singleTarget.getReference();
- EmulatedDispatchMethodDescriptor descriptor = emulatedVirtualRetarget.get(reference);
+ private InvokeRetargetingResult computeNonStaticRetarget(DexMethod singleTarget) {
+ EmulatedDispatchMethodDescriptor descriptor = emulatedVirtualRetarget.get(singleTarget);
if (descriptor != null) {
return new InvokeRetargetingResult(
true,
@@ -174,7 +174,7 @@
syntheticHelper.ensureEmulatedHolderDispatchMethod(descriptor, eventConsumer));
}
return InvokeRetargetingResult.createInvokeRetargetingResult(
- nonEmulatedVirtualRetarget.get(reference));
+ nonEmulatedVirtualRetarget.get(singleTarget));
}
private InvokeRetargetingResult computeSuperRetarget(DexEncodedMethod singleTarget) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
index 5a5768f..5519d95 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
@@ -18,7 +19,6 @@
import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
import com.android.tools.r8.synthesis.SyntheticClassBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.DescriptorUtils;
import java.util.LinkedHashMap;
public class DesugaredLibraryRetargeterSyntheticHelper {
@@ -37,7 +37,8 @@
private DexMethod emulatedHolderDispatchMethod(DexType holder, DerivedMethod method) {
assert method.getHolderKind() == SyntheticKind.RETARGET_CLASS;
- return appView.dexItemFactory().createMethod(holder, method.getProto(), method.getName());
+ DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
+ return appView.dexItemFactory().createMethod(holder, newProto, method.getName());
}
DexMethod emulatedInterfaceDispatchMethod(DexType holder, DerivedMethod method) {
@@ -83,10 +84,7 @@
context,
appView,
classBuilder -> buildHolderDispatchMethod(classBuilder, itfClass, descriptor),
- clazz -> {
- eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
- rewriteType(clazz.type);
- });
+ eventConsumer::acceptDesugaredLibraryRetargeterDispatchClasspathClass);
}
DexMethod dispatchMethod =
emulatedHolderDispatchMethod(syntheticClass.type, emulatedDispatchMethod);
@@ -110,10 +108,7 @@
holderContext,
appView,
classBuilder -> buildHolderDispatchMethod(classBuilder, itfClass, descriptor),
- clazz -> {
- eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
- rewriteType(clazz.type);
- });
+ eventConsumer::acceptDesugaredLibraryRetargeterDispatchProgramClass);
}
public DexClass ensureEmulatedInterfaceDispatchMethod(
@@ -136,10 +131,7 @@
context,
appView,
classBuilder -> buildInterfaceDispatchMethod(classBuilder, descriptor),
- clazz -> {
- eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
- rewriteType(clazz.type);
- });
+ eventConsumer::acceptDesugaredLibraryRetargeterDispatchClasspathClass);
}
public DexClass ensureEmulatedInterfaceDispatchMethod(
@@ -156,10 +148,7 @@
itfContext,
appView,
classBuilder -> buildInterfaceDispatchMethod(classBuilder, descriptor),
- clazz -> {
- eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
- rewriteType(clazz.type);
- });
+ eventConsumer::acceptDesugaredLibraryRetargeterDispatchProgramClass);
}
private void buildInterfaceDispatchMethod(
@@ -214,12 +203,4 @@
methodSig.getHolderType(), forwardingMethod, itfMethod, new LinkedHashMap<>(), appView)
.generateCfCode();
}
-
- private void rewriteType(DexType type) {
- String newName =
- appView.options().desugaredLibrarySpecification.convertJavaNameToDesugaredLibrary(type);
- DexType newType =
- appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(newName));
- appView.rewritePrefix.rewriteType(type, newType);
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
index eb38e9e..9e1f5e3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
@@ -115,12 +115,8 @@
DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
DerivedMethod interfaceMethod =
new DerivedMethod(methodReference, SyntheticKind.RETARGET_INTERFACE);
- DexMethod dispatchDexMethod =
- appView
- .dexItemFactory()
- .createMethod(methodReference.getHolderType(), newProto, methodName);
DerivedMethod dispatchMethod =
- new DerivedMethod(dispatchDexMethod, SyntheticKind.RETARGET_CLASS);
+ new DerivedMethod(methodReference, SyntheticKind.RETARGET_CLASS);
emulatedVirtualRetarget.put(
methodReference,
new EmulatedDispatchMethodDescriptor(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
new file mode 100644
index 0000000..2411ba0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
@@ -0,0 +1,183 @@
+// Copyright (c) 2022, 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.desugar.desugaredlibrary.specificationconversion;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class HumanToMachineEmulatedInterfaceConverter {
+
+ private final AppInfoWithClassHierarchy appInfo;
+ private Map<DexType, List<DexType>> emulatedInterfaceHierarchy;
+
+ public HumanToMachineEmulatedInterfaceConverter(AppInfoWithClassHierarchy appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ public void convertEmulatedInterfaces(
+ HumanRewritingFlags rewritingFlags,
+ AppInfoWithClassHierarchy appInfo,
+ MachineRewritingFlags.Builder builder) {
+ Map<DexType, DexType> emulateInterfaces = rewritingFlags.getEmulateLibraryInterface();
+ Set<DexMethod> dontRewriteInvocation = rewritingFlags.getDontRewriteInvocation();
+ emulatedInterfaceHierarchy = processEmulatedInterfaceHierarchy(appInfo, emulateInterfaces);
+ for (DexType itf : emulateInterfaces.keySet()) {
+ DexProgramClass itfClass = appInfo.contextIndependentDefinitionFor(itf).asProgramClass();
+ assert itfClass != null;
+ Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods = new IdentityHashMap<>();
+ itfClass.forEachProgramVirtualMethodMatching(
+ m -> m.isDefaultMethod() && !dontRewriteInvocation.contains(m.getReference()),
+ method ->
+ emulatedMethods.put(
+ method.getReference(),
+ computeEmulatedDispatchDescriptor(
+ method.getReference(), rewritingFlags, appInfo)));
+ builder.putEmulatedInterface(
+ itf, new EmulatedInterfaceDescriptor(emulateInterfaces.get(itf), emulatedMethods));
+ }
+ }
+
+ private EmulatedDispatchMethodDescriptor computeEmulatedDispatchDescriptor(
+ DexMethod method, HumanRewritingFlags rewritingFlags, AppInfoWithClassHierarchy appInfo) {
+ DerivedMethod forwardingMethod = new DerivedMethod(method, SyntheticKind.COMPANION_CLASS);
+ DexMethod itfDexMethod =
+ appInfo
+ .dexItemFactory()
+ .createMethod(
+ rewritingFlags.getEmulateLibraryInterface().get(method.getHolderType()),
+ method.getProto(),
+ method.getName());
+ DerivedMethod interfaceMethod = new DerivedMethod(itfDexMethod);
+ DerivedMethod dispatchMethod =
+ new DerivedMethod(method, SyntheticKind.EMULATED_INTERFACE_CLASS);
+ LinkedHashMap<DexType, DerivedMethod> dispatchCases = getDispatchCases(rewritingFlags, method);
+ return new EmulatedDispatchMethodDescriptor(
+ interfaceMethod, dispatchMethod, forwardingMethod, dispatchCases);
+ }
+
+ private LinkedHashMap<DexType, DerivedMethod> getDispatchCases(
+ HumanRewritingFlags rewritingFlags, DexMethod method) {
+ // To properly emulate the library interface call, we need to compute the interfaces
+ // inheriting from the interface and manually implement the dispatch with instance of.
+ // The list guarantees that an interface is always after interfaces it extends,
+ // hence reverse iteration.
+ List<DexType> subInterfaces = emulatedInterfaceHierarchy.get(method.getHolderType());
+ LinkedHashMap<DexType, DerivedMethod> extraDispatchCases = new LinkedHashMap<>();
+ // Retarget core lib emulated dispatch handled as part of emulated interface dispatch.
+ Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetCoreLibMember();
+ for (DexMethod retarget : retargetCoreLibMember.keySet()) {
+ if (retarget.match(method)) {
+ DexClass inClass = appInfo.definitionFor(retarget.getHolderType());
+ if (inClass != null && implementsInterface(inClass, method.getHolderType())) {
+ DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(retarget);
+ DexMethod forwardingDexMethod =
+ appInfo
+ .dexItemFactory()
+ .createMethod(retargetCoreLibMember.get(retarget), newProto, retarget.getName());
+ extraDispatchCases.put(retarget.getHolderType(), new DerivedMethod(forwardingDexMethod));
+ }
+ }
+ }
+ if (subInterfaces != null) {
+ for (int i = subInterfaces.size() - 1; i >= 0; i--) {
+ DexClass subInterfaceClass = appInfo.definitionFor(subInterfaces.get(i));
+ assert subInterfaceClass != null;
+ assert subInterfaceClass.isProgramClass();
+ // Else computation of subInterface would have failed.
+ // if the method is implemented, extra dispatch is required.
+ DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method);
+ if (result != null && !result.isAbstract()) {
+ assert result.isDefaultMethod();
+ DexMethod reference = result.getReference();
+ extraDispatchCases.put(
+ subInterfaceClass.type, new DerivedMethod(reference, SyntheticKind.COMPANION_CLASS));
+ }
+ }
+ } else {
+ assert extraDispatchCases.size() <= 1;
+ }
+ return extraDispatchCases;
+ }
+
+ private boolean implementsInterface(DexClass clazz, DexType interfaceType) {
+ WorkList<DexType> workList =
+ WorkList.newIdentityWorkList(Arrays.asList(clazz.interfaces.values));
+ while (!workList.isEmpty()) {
+ DexType next = workList.next();
+ if (interfaceType == next) {
+ return true;
+ }
+ DexClass nextClass = appInfo.definitionFor(next);
+ if (nextClass != null) {
+ workList.addIfNotSeen(nextClass.interfaces.values);
+ }
+ }
+ return false;
+ }
+
+ private Map<DexType, List<DexType>> processEmulatedInterfaceHierarchy(
+ AppInfoWithClassHierarchy appInfo, Map<DexType, DexType> emulateInterfaces) {
+ Map<DexType, List<DexType>> emulatedInterfacesHierarchy = new IdentityHashMap<>();
+ Set<DexType> processed = Sets.newIdentityHashSet();
+ ArrayList<DexType> emulatedInterfacesSorted = new ArrayList<>(emulateInterfaces.keySet());
+ emulatedInterfacesSorted.sort(DexType::compareTo);
+ for (DexType interfaceType : emulatedInterfacesSorted) {
+ processEmulatedInterfaceHierarchy(
+ appInfo, emulateInterfaces, interfaceType, processed, emulatedInterfacesHierarchy);
+ }
+ return emulatedInterfacesHierarchy;
+ }
+
+ private void processEmulatedInterfaceHierarchy(
+ AppInfoWithClassHierarchy appInfo,
+ Map<DexType, DexType> emulateInterfaces,
+ DexType interfaceType,
+ Set<DexType> processed,
+ Map<DexType, List<DexType>> emulatedInterfacesHierarchy) {
+ if (processed.contains(interfaceType)) {
+ return;
+ }
+ emulatedInterfacesHierarchy.put(interfaceType, new ArrayList<>());
+ processed.add(interfaceType);
+ DexClass theInterface = appInfo.definitionFor(interfaceType);
+ if (theInterface == null) {
+ return;
+ }
+ WorkList<DexType> workList =
+ WorkList.newIdentityWorkList(Arrays.asList(theInterface.interfaces.values));
+ while (!workList.isEmpty()) {
+ DexType next = workList.next();
+ if (emulateInterfaces.containsKey(next)) {
+ processEmulatedInterfaceHierarchy(
+ appInfo, emulateInterfaces, next, processed, emulatedInterfacesHierarchy);
+ emulatedInterfacesHierarchy.get(next).add(interfaceType);
+ DexClass nextClass = appInfo.definitionFor(next);
+ if (nextClass != null) {
+ workList.addIfNotSeen(nextClass.interfaces.values);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
new file mode 100644
index 0000000..6ff7428
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2022, 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.desugar.desugaredlibrary.specificationconversion;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public class HumanToMachinePrefixConverter {
+
+ private final AppInfoWithClassHierarchy appInfo;
+
+ public HumanToMachinePrefixConverter(AppInfoWithClassHierarchy appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ private DexString toDescriptorPrefix(String prefix) {
+ return appInfo
+ .dexItemFactory()
+ .createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
+ }
+
+ public void convertPrefixFlags(
+ HumanRewritingFlags rewritingFlags,
+ MachineRewritingFlags.Builder builder,
+ String synthesizedPrefix) {
+ Map<DexString, DexString> descriptorPrefix = convertRewritePrefix(rewritingFlags);
+ rewriteClasses(descriptorPrefix, builder);
+ rewriteValues(descriptorPrefix, builder, rewritingFlags.getRetargetCoreLibMember());
+ rewriteValues(descriptorPrefix, builder, rewritingFlags.getCustomConversions());
+ rewriteEmulatedInterface(builder, rewritingFlags.getEmulateLibraryInterface());
+ rewriteRetargetKeys(builder, rewritingFlags.getRetargetCoreLibMember(), synthesizedPrefix);
+ }
+
+ public DexType convertJavaNameToDesugaredLibrary(DexType type, String prefix) {
+ String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(prefix);
+ String interfaceType = type.toString();
+ int firstPackage = interfaceType.indexOf('.');
+ return appInfo
+ .dexItemFactory()
+ .createType(
+ DescriptorUtils.javaTypeToDescriptor(
+ convertedPrefix + interfaceType.substring(firstPackage + 1)));
+ }
+
+ private void rewriteRetargetKeys(
+ MachineRewritingFlags.Builder builder, Map<DexMethod, DexType> retarget, String prefix) {
+ for (DexMethod dexMethod : retarget.keySet()) {
+ DexType type = convertJavaNameToDesugaredLibrary(dexMethod.holder, prefix);
+ builder.rewriteDerivedTypeOnly(dexMethod.holder, type);
+ }
+ }
+
+ private void rewriteEmulatedInterface(
+ MachineRewritingFlags.Builder builder, Map<DexType, DexType> emulateLibraryInterface) {
+ emulateLibraryInterface.forEach(builder::rewriteDerivedTypeOnly);
+ }
+
+ private void rewriteValues(
+ Map<DexString, DexString> descriptorPrefix,
+ MachineRewritingFlags.Builder builder,
+ Map<?, DexType> flags) {
+ for (DexType type : flags.values()) {
+ DexType rewrittenType = rewrittenType(descriptorPrefix, type);
+ if (rewrittenType != null) {
+ builder.rewriteType(type, rewrittenType);
+ }
+ }
+ }
+
+ private void rewriteClasses(
+ Map<DexString, DexString> descriptorPrefix, MachineRewritingFlags.Builder builder) {
+ for (DexProgramClass clazz : appInfo.classes()) {
+ DexType type = clazz.type;
+ DexType rewrittenType = rewrittenType(descriptorPrefix, type);
+ if (rewrittenType != null) {
+ builder.rewriteType(type, rewrittenType);
+ }
+ }
+ }
+
+ private DexType rewrittenType(Map<DexString, DexString> descriptorPrefix, DexType type) {
+ DexString prefixToMatch = type.descriptor.withoutArray(appInfo.dexItemFactory());
+ for (DexString prefix : descriptorPrefix.keySet()) {
+ if (prefixToMatch.startsWith(prefix)) {
+ DexString rewrittenTypeDescriptor =
+ type.descriptor.withNewPrefix(
+ prefix, descriptorPrefix.get(prefix), appInfo.dexItemFactory());
+ return appInfo.dexItemFactory().createType(rewrittenTypeDescriptor);
+ }
+ }
+ return null;
+ }
+
+ private ImmutableMap<DexString, DexString> convertRewritePrefix(
+ HumanRewritingFlags rewritingFlags) {
+ Map<String, String> rewritePrefix = rewritingFlags.getRewritePrefix();
+ ImmutableMap.Builder<DexString, DexString> mapBuilder = ImmutableMap.builder();
+ for (String key : rewritePrefix.keySet()) {
+ mapBuilder.put(toDescriptorPrefix(key), toDescriptorPrefix(rewritePrefix.get(key)));
+ }
+ return mapBuilder.build();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
new file mode 100644
index 0000000..6dce945
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -0,0 +1,168 @@
+// Copyright (c) 2022, 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.desugar.desugaredlibrary.specificationconversion;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.TraversalContinuation;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+public class HumanToMachineRetargetConverter {
+
+ private final AppInfoWithClassHierarchy appInfo;
+ private SubtypingInfo subtypingInfo;
+
+ public HumanToMachineRetargetConverter(AppInfoWithClassHierarchy appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ public void convertRetargetFlags(
+ HumanRewritingFlags rewritingFlags, MachineRewritingFlags.Builder builder) {
+ subtypingInfo = SubtypingInfo.create(appInfo);
+ rewritingFlags
+ .getRetargetCoreLibMember()
+ .forEach(
+ (method, type) ->
+ convertRetargetCoreLibMemberFlag(builder, rewritingFlags, method, type));
+ }
+
+ private void convertRetargetCoreLibMemberFlag(
+ MachineRewritingFlags.Builder builder,
+ HumanRewritingFlags rewritingFlags,
+ DexMethod method,
+ DexType type) {
+ DexClass holder = appInfo.definitionFor(method.holder);
+ DexEncodedMethod foundMethod = holder.lookupMethod(method);
+ assert foundMethod != null;
+ if (foundMethod.isStatic()) {
+ convertStaticRetarget(builder, foundMethod, type);
+ return;
+ }
+ if (holder.isFinal() || foundMethod.isFinal()) {
+ convertNonEmulatedVirtualRetarget(builder, foundMethod, type);
+ return;
+ }
+ convertEmulatedVirtualRetarget(builder, rewritingFlags, foundMethod, type);
+ }
+
+ private void convertEmulatedVirtualRetarget(
+ MachineRewritingFlags.Builder builder,
+ HumanRewritingFlags rewritingFlags,
+ DexEncodedMethod src,
+ DexType type) {
+ if (isEmulatedInterfaceDispatch(src, appInfo, rewritingFlags)) {
+ // Handled by emulated interface dispatch.
+ return;
+ }
+ // TODO(b/184026720): Implement library boundaries.
+ DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(src.getReference());
+ DexMethod forwardingDexMethod =
+ appInfo.dexItemFactory().createMethod(type, newProto, src.getName());
+ DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
+ DerivedMethod interfaceMethod =
+ new DerivedMethod(src.getReference(), SyntheticKind.RETARGET_INTERFACE);
+ DerivedMethod dispatchMethod =
+ new DerivedMethod(src.getReference(), SyntheticKind.RETARGET_CLASS);
+ LinkedHashMap<DexType, DerivedMethod> dispatchCases = new LinkedHashMap<>();
+ assert validateNoOverride(src, appInfo, subtypingInfo);
+ builder.putEmulatedVirtualRetarget(
+ src.getReference(),
+ new EmulatedDispatchMethodDescriptor(
+ interfaceMethod, dispatchMethod, forwardingMethod, dispatchCases));
+ }
+
+ private boolean validateNoOverride(
+ DexEncodedMethod src, AppInfoWithClassHierarchy appInfo, SubtypingInfo subtypingInfo) {
+ for (DexType subtype : subtypingInfo.subtypes(src.getHolderType())) {
+ DexClass subclass = appInfo.definitionFor(subtype);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(subclass, src.getReference());
+ if (resolutionResult.isSuccessfulMemberResolutionResult()
+ && resolutionResult.getResolvedMethod().getReference() != src.getReference()) {
+ assert false; // Unsupported.
+ }
+ }
+ return true;
+ }
+
+ private boolean isEmulatedInterfaceDispatch(
+ DexEncodedMethod method,
+ AppInfoWithClassHierarchy appInfo,
+ HumanRewritingFlags humanRewritingFlags) {
+ // Answers true if this method is already managed through emulated interface dispatch.
+ Map<DexType, DexType> emulateLibraryInterface =
+ humanRewritingFlags.getEmulateLibraryInterface();
+ if (emulateLibraryInterface.isEmpty()) {
+ return false;
+ }
+ DexMethod methodToFind = method.getReference();
+ // Look-up all superclass and interfaces, if an emulated interface is found,
+ // and it implements the method, answers true.
+ DexClass dexClass = appInfo.definitionFor(method.getHolderType());
+ // Cannot retarget a method on a virtual method on an emulated interface.
+ assert !emulateLibraryInterface.containsKey(dexClass.getType());
+ return appInfo
+ .traverseSuperTypes(
+ dexClass,
+ (supertype, subclass, isSupertypeAnInterface) ->
+ TraversalContinuation.breakIf(
+ subclass.isInterface()
+ && emulateLibraryInterface.containsKey(subclass.getType())
+ && subclass.lookupMethod(methodToFind) != null))
+ .shouldBreak();
+ }
+
+ private void convertNonEmulatedRetarget(
+ DexEncodedMethod foundMethod,
+ DexType type,
+ AppInfoWithClassHierarchy appInfo,
+ SubtypingInfo subtypingInfo,
+ BiConsumer<DexMethod, DexMethod> consumer) {
+ DexMethod src = foundMethod.getReference();
+ DexMethod dest = src.withHolder(type, appInfo.dexItemFactory());
+ consumer.accept(src, dest);
+ for (DexType subtype : subtypingInfo.subtypes(foundMethod.getHolderType())) {
+ DexClass subclass = appInfo.definitionFor(subtype);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(subclass, src);
+ if (resolutionResult.isSuccessfulMemberResolutionResult()
+ && resolutionResult.getResolvedMethod().getReference() == src) {
+ consumer.accept(src.withHolder(subtype, appInfo.dexItemFactory()), dest);
+ }
+ }
+ }
+
+ private void convertNonEmulatedVirtualRetarget(
+ MachineRewritingFlags.Builder builder, DexEncodedMethod foundMethod, DexType type) {
+ convertNonEmulatedRetarget(
+ foundMethod,
+ type,
+ appInfo,
+ subtypingInfo,
+ (src, dest) ->
+ builder.putNonEmulatedVirtualRetarget(
+ src,
+ dest.withExtraArgumentPrepended(
+ foundMethod.getHolderType(), appInfo.dexItemFactory())));
+ }
+
+ private void convertStaticRetarget(
+ MachineRewritingFlags.Builder builder, DexEncodedMethod foundMethod, DexType type) {
+ convertNonEmulatedRetarget(
+ foundMethod, type, appInfo, subtypingInfo, builder::putStaticRetarget);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index d2227cb..bc3a663 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -9,31 +9,20 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.MethodResolutionResult;
-import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineTopLevelFlags;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.TraversalContinuation;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.LinkedHashMap;
-import java.util.Map;
import java.util.concurrent.ExecutorService;
-import java.util.function.BiConsumer;
public class HumanToMachineSpecificationConverter {
@@ -43,164 +32,49 @@
DexApplication app = readApp(androidLib, options);
AppView<?> appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
MachineRewritingFlags machineRewritingFlags =
- convertRewritingFlags(humanSpec.getRewritingFlags(), appView.appInfoForDesugaring());
+ convertRewritingFlags(
+ humanSpec.getSynthesizedLibraryClassesPackagePrefix(),
+ humanSpec.getRewritingFlags(),
+ appView.appInfoForDesugaring());
+ MachineTopLevelFlags topLevelFlags = convertTopLevelFlags(humanSpec.getTopLevelFlags());
return new MachineDesugaredLibrarySpecification(
- humanSpec.isLibraryCompilation(), machineRewritingFlags);
+ humanSpec.isLibraryCompilation(), topLevelFlags, machineRewritingFlags);
+ }
+
+ private MachineTopLevelFlags convertTopLevelFlags(HumanTopLevelFlags topLevelFlags) {
+ return new MachineTopLevelFlags(
+ topLevelFlags.getRequiredCompilationAPILevel(),
+ topLevelFlags.getSynthesizedLibraryClassesPackagePrefix(),
+ topLevelFlags.getIdentifier(),
+ topLevelFlags.getJsonSource(),
+ topLevelFlags.supportAllCallbacksFromLibrary(),
+ topLevelFlags.getExtraKeepRules());
}
private MachineRewritingFlags convertRewritingFlags(
- HumanRewritingFlags rewritingFlags, AppInfoWithClassHierarchy appInfo) {
+ String synthesizedPrefix,
+ HumanRewritingFlags rewritingFlags,
+ AppInfoWithClassHierarchy appInfo) {
MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
- SubtypingInfo subtypingInfo = new SubtypingInfo(appInfo);
+ new HumanToMachineRetargetConverter(appInfo).convertRetargetFlags(rewritingFlags, builder);
+ new HumanToMachineEmulatedInterfaceConverter(appInfo)
+ .convertEmulatedInterfaces(rewritingFlags, appInfo, builder);
+ new HumanToMachinePrefixConverter(appInfo)
+ .convertPrefixFlags(rewritingFlags, builder, synthesizedPrefix);
+ new HumanToMachineWrapperConverter(appInfo).convertWrappers(rewritingFlags, builder);
rewritingFlags
- .getRetargetCoreLibMember()
+ .getCustomConversions()
.forEach(
- (method, type) ->
- convertRetargetCoreLibMemberFlag(
- builder, rewritingFlags, method, type, appInfo, subtypingInfo));
+ (type, conversionType) ->
+ builder.putCustomConversion(
+ type, conversionType, appInfo.dexItemFactory().convertMethodName));
+ for (DexType type : rewritingFlags.getDontRetargetLibMember()) {
+ builder.addDontRetarget(type);
+ }
+ rewritingFlags.getBackportCoreLibraryMember().forEach(builder::putLegacyBackport);
return builder.build();
}
- private void convertRetargetCoreLibMemberFlag(
- MachineRewritingFlags.Builder builder,
- HumanRewritingFlags rewritingFlags,
- DexMethod method,
- DexType type,
- AppInfoWithClassHierarchy appInfo,
- SubtypingInfo subtypingInfo) {
- DexClass holder = appInfo.definitionFor(method.holder);
- DexEncodedMethod foundMethod = holder.lookupMethod(method);
- assert foundMethod != null;
- if (foundMethod.isStatic()) {
- convertStaticRetarget(builder, foundMethod, type, appInfo, subtypingInfo);
- return;
- }
- if (holder.isFinal() || foundMethod.isFinal()) {
- convertNonEmulatedVirtualRetarget(builder, foundMethod, type, appInfo, subtypingInfo);
- return;
- }
- convertEmulatedVirtualRetarget(
- builder, rewritingFlags, foundMethod, type, appInfo, subtypingInfo);
- }
-
- private void convertEmulatedVirtualRetarget(
- MachineRewritingFlags.Builder builder,
- HumanRewritingFlags rewritingFlags,
- DexEncodedMethod src,
- DexType type,
- AppInfoWithClassHierarchy appInfo,
- SubtypingInfo subtypingInfo) {
- if (isEmulatedInterfaceDispatch(src, appInfo, rewritingFlags)) {
- // Handled by emulated interface dispatch.
- return;
- }
- // TODO(b/184026720): Implement library boundaries.
- DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(src.getReference());
- DexMethod forwardingDexMethod =
- appInfo.dexItemFactory().createMethod(type, newProto, src.getName());
- DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
- DerivedMethod interfaceMethod =
- new DerivedMethod(src.getReference(), SyntheticKind.RETARGET_INTERFACE);
- DexMethod dispatchDexMethod =
- appInfo.dexItemFactory().createMethod(src.getHolderType(), newProto, src.getName());
- DerivedMethod dispatchMethod =
- new DerivedMethod(dispatchDexMethod, SyntheticKind.RETARGET_CLASS);
- LinkedHashMap<DexType, DerivedMethod> dispatchCases = new LinkedHashMap<>();
- assert validateNoOverride(src, appInfo, subtypingInfo);
- builder.putEmulatedVirtualRetarget(
- src.getReference(),
- new EmulatedDispatchMethodDescriptor(
- interfaceMethod, dispatchMethod, forwardingMethod, dispatchCases));
- }
-
- private boolean validateNoOverride(
- DexEncodedMethod src, AppInfoWithClassHierarchy appInfo, SubtypingInfo subtypingInfo) {
- for (DexType subtype : subtypingInfo.subtypes(src.getHolderType())) {
- DexClass subclass = appInfo.definitionFor(subtype);
- MethodResolutionResult resolutionResult =
- appInfo.resolveMethodOn(subclass, src.getReference());
- if (resolutionResult.isSuccessfulMemberResolutionResult()
- && resolutionResult.getResolvedMethod().getReference() != src.getReference()) {
- assert false; // Unsupported.
- }
- }
- return true;
- }
-
- private boolean isEmulatedInterfaceDispatch(
- DexEncodedMethod method,
- AppInfoWithClassHierarchy appInfo,
- HumanRewritingFlags humanRewritingFlags) {
- // Answers true if this method is already managed through emulated interface dispatch.
- Map<DexType, DexType> emulateLibraryInterface =
- humanRewritingFlags.getEmulateLibraryInterface();
- if (emulateLibraryInterface.isEmpty()) {
- return false;
- }
- DexMethod methodToFind = method.getReference();
- // Look-up all superclass and interfaces, if an emulated interface is found,
- // and it implements the method, answers true.
- DexClass dexClass = appInfo.definitionFor(method.getHolderType());
- // Cannot retarget a method on a virtual method on an emulated interface.
- assert !emulateLibraryInterface.containsKey(dexClass.getType());
- return appInfo
- .traverseSuperTypes(
- dexClass,
- (supertype, subclass, isSupertypeAnInterface) ->
- TraversalContinuation.breakIf(
- subclass.isInterface()
- && emulateLibraryInterface.containsKey(subclass.getType())
- && subclass.lookupMethod(methodToFind) != null))
- .shouldBreak();
- }
-
- private void convertNonEmulatedRetarget(
- DexEncodedMethod foundMethod,
- DexType type,
- AppInfoWithClassHierarchy appInfo,
- SubtypingInfo subtypingInfo,
- BiConsumer<DexMethod, DexMethod> consumer) {
- DexMethod src = foundMethod.getReference();
- DexMethod dest = src.withHolder(type, appInfo.dexItemFactory());
- consumer.accept(src, dest);
- for (DexType subtype : subtypingInfo.subtypes(foundMethod.getHolderType())) {
- DexClass subclass = appInfo.definitionFor(subtype);
- MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(subclass, src);
- if (resolutionResult.isSuccessfulMemberResolutionResult()
- && resolutionResult.getResolvedMethod().getReference() == src) {
- consumer.accept(src.withHolder(subtype, appInfo.dexItemFactory()), dest);
- }
- }
- }
-
- private void convertNonEmulatedVirtualRetarget(
- MachineRewritingFlags.Builder builder,
- DexEncodedMethod foundMethod,
- DexType type,
- AppInfoWithClassHierarchy appInfo,
- SubtypingInfo subtypingInfo) {
- convertNonEmulatedRetarget(
- foundMethod,
- type,
- appInfo,
- subtypingInfo,
- (src, dest) ->
- builder.putNonEmulatedVirtualRetarget(
- src,
- dest.withExtraArgumentPrepended(
- foundMethod.getHolderType(), appInfo.dexItemFactory())));
- }
-
- private void convertStaticRetarget(
- MachineRewritingFlags.Builder builder,
- DexEncodedMethod foundMethod,
- DexType type,
- AppInfoWithClassHierarchy appInfo,
- SubtypingInfo subtypingInfo) {
- convertNonEmulatedRetarget(
- foundMethod, type, appInfo, subtypingInfo, builder::putStaticRetarget);
- }
-
private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
AndroidApp androidApp = AndroidApp.builder().addProgramFile(androidLib).build();
ApplicationReader applicationReader =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
new file mode 100644
index 0000000..db87890
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2022, 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.desugar.desugaredlibrary.specificationconversion;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexClass;
+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.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class HumanToMachineWrapperConverter {
+
+ private final AppInfoWithClassHierarchy appInfo;
+
+ public HumanToMachineWrapperConverter(AppInfoWithClassHierarchy appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ public void convertWrappers(
+ HumanRewritingFlags rewritingFlags, MachineRewritingFlags.Builder builder) {
+ for (DexType wrapperConversion : rewritingFlags.getWrapperConversions()) {
+ DexClass wrapperClass = appInfo.definitionFor(wrapperConversion);
+ assert wrapperClass != null;
+ List<DexMethod> methods = allImplementedMethods(wrapperClass);
+ methods.sort(DexMethod::compareTo);
+ builder.addWrapper(wrapperConversion, methods);
+ }
+ }
+
+ private List<DexMethod> allImplementedMethods(DexClass wrapperClass) {
+ LinkedList<DexClass> workList = new LinkedList<>();
+ List<DexMethod> implementedMethods = new ArrayList<>();
+ workList.add(wrapperClass);
+ while (!workList.isEmpty()) {
+ DexClass dexClass = workList.removeFirst();
+ for (DexEncodedMethod virtualMethod : dexClass.virtualMethods()) {
+ if (!virtualMethod.isPrivateMethod()) {
+ assert virtualMethod.isProtectedMethod() || virtualMethod.isPublicMethod();
+ boolean alreadyAdded = false;
+ // This looks quadratic but given the size of the collections met in practice for
+ // desugared libraries (Max ~15) it does not matter.
+ for (DexMethod alreadyImplementedMethod : implementedMethods) {
+ if (alreadyImplementedMethod.match(virtualMethod.getReference())) {
+ alreadyAdded = true;
+ break;
+ }
+ }
+ if (!alreadyAdded) {
+ assert !virtualMethod.isFinal() : "Cannot wrap final method " + virtualMethod;
+ implementedMethods.add(virtualMethod.getReference());
+ }
+ }
+ }
+ for (DexType itf : dexClass.interfaces.values) {
+ DexClass itfClass = appInfo.definitionFor(itf);
+ if (itfClass != null) {
+ workList.add(itfClass);
+ }
+ }
+ if (dexClass.superType != appInfo.dexItemFactory().objectType) {
+ DexClass superClass = appInfo.definitionFor(dexClass.superType);
+ assert superClass != null; // Cannot be null since we started from a LibraryClass.
+ workList.add(superClass);
+ }
+ }
+ return implementedMethods;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 3a91fef..758812e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -576,7 +576,7 @@
Map<DexType, GenericSignature.ClassTypeSignature> extraInterfaceSignatures) {
// TODO(b/182329331): Only handle type arguments for Cf to Cf desugar.
if (appView.options().cfToCfDesugar && clazz.validInterfaceSignatures()) {
- clazz.forEachImmediateSupertype(
+ clazz.forEachImmediateSupertypeWithSignature(
(type, signature) -> {
if (emulatesInterfaces.contains(type)) {
extraInterfaceSignatures.put(
@@ -589,7 +589,7 @@
});
} else {
clazz.forEachImmediateSupertype(
- (type) -> {
+ type -> {
if (emulatesInterfaces.contains(type)) {
extraInterfaceSignatures.put(
type, new GenericSignature.ClassTypeSignature(helper.getEmulatedInterface(type)));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index a40d69a..d30f717 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -43,7 +43,6 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.synthesis.SyntheticNaming;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.structural.Ordered;
@@ -160,8 +159,6 @@
Map<DexType, DexType> emulateLibraryInterface =
options.desugaredLibrarySpecification.getEmulateLibraryInterface();
for (DexType interfaceType : emulateLibraryInterface.keySet()) {
- addRewriteRulesForEmulatedInterface(
- interfaceType, emulateLibraryInterface.get(interfaceType).toSourceString());
DexClass emulatedInterfaceClass = appView.definitionFor(interfaceType);
if (emulatedInterfaceClass != null) {
for (DexEncodedMethod encodedMethod :
@@ -172,35 +169,6 @@
}
}
- void addRewriteRulesForEmulatedInterface(
- DexType emulatedInterface, String rewrittenEmulatedInterface) {
- addCompanionClassRewriteRule(emulatedInterface, rewrittenEmulatedInterface);
- appView.rewritePrefix.rewriteType(
- InterfaceDesugaringSyntheticHelper.getEmulateLibraryInterfaceClassType(
- emulatedInterface, factory),
- factory.createType(
- DescriptorUtils.javaTypeToDescriptor(
- rewrittenEmulatedInterface
- + InterfaceDesugaringSyntheticHelper.EMULATE_LIBRARY_CLASS_NAME_SUFFIX)));
- }
-
- private void addCompanionClassRewriteRule(DexType interfaceType, String rewrittenType) {
- addCompanionClassRewriteRule(interfaceType, rewrittenType, appView);
- }
-
- static void addCompanionClassRewriteRule(
- DexType interfaceType, String rewrittenType, AppView<?> appView) {
- appView.rewritePrefix.rewriteType(
- InterfaceDesugaringSyntheticHelper.getCompanionClassType(
- interfaceType, appView.dexItemFactory()),
- appView
- .dexItemFactory()
- .createType(
- DescriptorUtils.javaTypeToDescriptor(
- rewrittenType
- + InterfaceDesugaringSyntheticHelper.COMPANION_CLASS_NAME_SUFFIX)));
- }
-
private boolean isAlreadyDesugared(CfInvoke invoke, ProgramMethod context) {
return Iterables.any(
precedingDesugarings, desugaring -> desugaring.needsDesugaring(invoke, context));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 82f80e8..3aec49b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -414,7 +414,7 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
return extraNewMethodSignatures.getRepresentativeKeyOrDefault(
method, newMethodSignatures.getRepresentativeKeyOrDefault(method, method));
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 55cf273..3e8b5cb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -4,21 +4,25 @@
package com.android.tools.r8.ir.desugar.itf;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Iterables;
@@ -116,28 +120,34 @@
method ->
builder.addMethod(
methodBuilder ->
- synthesizeEmulatedInterfaceMethod(method, emulatedInterface, methodBuilder)));
- assert builder.getType()
- == InterfaceDesugaringSyntheticHelper.getEmulateLibraryInterfaceClassType(
- emulatedInterface.type, appView.dexItemFactory());
+ synthesizeEmulatedInterfaceMethod(
+ method, emulatedInterface, builder.getType(), methodBuilder)));
+ }
+
+ private DexMethod emulatedMethod(DerivedMethod method, DexType holder) {
+ assert method.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
+ DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
+ return appView.dexItemFactory().createMethod(holder, newProto, method.getName());
+ }
+
+ private DexMethod interfaceMethod(DerivedMethod method) {
+ assert method.getHolderKind() == null;
+ return method.getMethod();
}
private void synthesizeEmulatedInterfaceMethod(
- ProgramMethod method, DexProgramClass theInterface, SyntheticMethodBuilder methodBuilder) {
+ ProgramMethod method,
+ DexProgramClass theInterface,
+ DexType dispatchType,
+ SyntheticMethodBuilder methodBuilder) {
assert !method.getDefinition().isStatic();
+ if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
+ synthesizeEmulatedInterfaceMethodFromMachineSpecification(
+ method, theInterface, dispatchType, methodBuilder);
+ return;
+ }
DexMethod emulatedMethod = helper.emulateInterfaceLibraryMethod(method);
- methodBuilder
- .setName(emulatedMethod.getName())
- .setProto(emulatedMethod.getProto())
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setCode(
- emulatedInterfaceMethod ->
- synthesizeCfCode(method.asProgramMethod(), theInterface, emulatedInterfaceMethod));
- }
-
- private CfCode synthesizeCfCode(
- ProgramMethod method, DexProgramClass theInterface, DexMethod emulatedInterfaceMethod) {
- DexMethod libraryMethod =
+ DexMethod itfMethod =
method
.getReference()
.withHolder(helper.getEmulatedInterface(theInterface.type), appView.dexItemFactory());
@@ -145,13 +155,84 @@
helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
LinkedHashMap<DexType, DexMethod> extraDispatchCases =
getDispatchCases(method, theInterface, companionMethod);
- return new EmulateDispatchSyntheticCfCodeProvider(
- emulatedInterfaceMethod.getHolderType(),
- companionMethod,
- libraryMethod,
- extraDispatchCases,
- appView)
- .generateCfCode();
+ methodBuilder
+ .setName(emulatedMethod.getName())
+ .setProto(emulatedMethod.getProto())
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ emulatedInterfaceMethod ->
+ new EmulateDispatchSyntheticCfCodeProvider(
+ emulatedMethod.getHolderType(),
+ companionMethod,
+ itfMethod,
+ extraDispatchCases,
+ appView)
+ .generateCfCode());
+ }
+
+ private void synthesizeEmulatedInterfaceMethodFromMachineSpecification(
+ ProgramMethod method,
+ DexProgramClass theInterface,
+ DexType dispatchType,
+ SyntheticMethodBuilder methodBuilder) {
+ EmulatedInterfaceDescriptor emulatedInterfaceDescriptor =
+ appView
+ .options()
+ .testing
+ .machineDesugaredLibrarySpecification
+ .getRewritingFlags()
+ .getEmulatedInterfaces()
+ .get(theInterface.type);
+ EmulatedDispatchMethodDescriptor descriptor =
+ emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference());
+ DexMethod emulatedMethod = emulatedMethod(descriptor.getEmulatedDispatchMethod(), dispatchType);
+ DexMethod itfMethod = interfaceMethod(descriptor.getInterfaceMethod());
+ // TODO(b/184026720): Adapt to use the forwarding method.
+ DerivedMethod forwardingMethod = descriptor.getForwardingMethod();
+ assert forwardingMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
+ assert forwardingMethod.getMethod() == method.getReference();
+ DexMethod companionMethod =
+ helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
+ LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
+ methodBuilder
+ .setName(descriptor.getEmulatedDispatchMethod().getName())
+ .setProto(descriptor.getEmulatedDispatchMethod().getProto())
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ emulatedInterfaceMethod ->
+ new EmulateDispatchSyntheticCfCodeProvider(
+ emulatedMethod.getHolderType(),
+ companionMethod,
+ itfMethod,
+ extraDispatchCases,
+ appView)
+ .generateCfCode());
+ }
+
+ private LinkedHashMap<DexType, DexMethod> resolveDispatchCases(
+ EmulatedDispatchMethodDescriptor descriptor) {
+ LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
+ descriptor
+ .getDispatchCases()
+ .forEach(
+ (type, derivedMethod) -> {
+ DexMethod caseMethod;
+ if (derivedMethod.getHolderKind() == null) {
+ caseMethod = derivedMethod.getMethod();
+ } else {
+ assert derivedMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
+ ProgramMethod resolvedProgramMethod =
+ appView
+ .appInfoForDesugaring()
+ .resolveMethod(derivedMethod.getMethod(), true)
+ .getResolvedProgramMethod();
+ caseMethod =
+ InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
+ resolvedProgramMethod.getReference(), appView.dexItemFactory());
+ }
+ extraDispatchCases.put(type, caseMethod);
+ });
+ return extraDispatchCases;
}
private LinkedHashMap<DexType, DexMethod> getDispatchCases(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 04ba47d..5dbfaba 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -14,11 +14,13 @@
import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
@@ -134,6 +136,23 @@
if (current.isInvoke() && !current.outValue().isUsed()) {
current.setOutValue(null);
}
+ if (current.isStaticGet() && !current.outValue().isUsed() && appView.hasLiveness()) {
+ Box<InitClass> initClass = new Box<>();
+ if (iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ appView.withLiveness(),
+ code,
+ current.asStaticGet().getField().getHolderType(),
+ initClass::set)) {
+ if (initClass.isSet()) {
+ // Apply dead code remover to the new init-class instruction.
+ current = iterator.previous();
+ assert current == initClass.get();
+ } else {
+ // Instruction removed.
+ continue;
+ }
+ }
+ }
}
DeadInstructionResult deadInstructionResult = current.canBeDeadCode(appView, code);
if (deadInstructionResult.isNotDead()) {
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 2c185bf..785c461 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
@@ -1099,7 +1099,7 @@
return false;
}
} else if (invoke.isInvokeStatic()) {
- if (!iterator.replaceCurrentInstructionByInitClassIfPossible(
+ if (!iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
appView, code, resolvedMethod.getHolderType())) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 755f334..4807cda 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -246,7 +246,7 @@
iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, code.context());
} else if (current.isStaticGet()) {
StaticGet staticGet = current.asStaticGet();
- iterator.replaceCurrentInstructionByInitClassIfPossible(
+ iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
appView, code, staticGet.getField().holder);
}
replacement.setPosition(position);
@@ -333,7 +333,7 @@
if (invoke.isInvokeMethodWithReceiver()) {
iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context);
} else if (invoke.isInvokeStatic() && singleTarget != null) {
- iterator.replaceCurrentInstructionByInitClassIfPossible(
+ iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
appView, code, singleTarget.getHolderType());
}
@@ -440,7 +440,7 @@
iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context);
} else {
assert current.isStaticGet();
- iterator.replaceCurrentInstructionByInitClassIfPossible(
+ iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
appView, code, target.getHolderType());
}
@@ -491,7 +491,8 @@
return;
}
- iterator.replaceCurrentInstructionByInitClassIfPossible(appView, code, field.getHolderType());
+ iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ appView, code, field.getHolderType());
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
index 4093846..3edb51f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
@@ -1681,7 +1681,6 @@
builder.addNonThisArgument(i, typeLattice);
}
}
- builder.flushArgumentInstructions();
}
@Override
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 056d9c6..0a0bed9 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
@@ -181,7 +181,7 @@
DexEncodedField field = fieldResolutionResult.getResolvedField();
FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
DynamicType dynamicType = optimizationInfo.getDynamicType();
- if (!dynamicType.isExactClassType()) {
+ if (!dynamicType.isExactClassType() || !dynamicType.getNullability().isDefinitelyNotNull()) {
return EligibilityStatus.NOT_ELIGIBLE;
}
eligibleClass =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
index 1b9d97b..1583873 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
@@ -8,10 +8,9 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
@@ -19,7 +18,7 @@
import com.android.tools.r8.ir.conversion.PostMethodProcessor.Builder;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
+import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -59,13 +58,11 @@
}
@Override
- public Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
- return Sets.newIdentityHashSet();
- }
-
- @Override
- public void rewriteNullCheck(InstructionListIterator iterator, InvokeMethod invoke) {
- // Intentionally empty.
+ public Set<Phi> rewriteCode(
+ IRCode code,
+ MethodProcessor methodProcessor,
+ RewrittenPrototypeDescription prototypeChanges) {
+ return Collections.emptySet();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 4179fed..8ff6293 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -8,10 +8,9 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
@@ -44,9 +43,9 @@
public abstract void recordEnumState(DexProgramClass clazz, StaticFieldValues staticFieldValues);
- public abstract Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor);
+ public abstract Set<Phi> rewriteCode(
+ IRCode code, MethodProcessor methodProcessor, RewrittenPrototypeDescription prototypeChanges);
- public abstract void rewriteNullCheck(InstructionListIterator iterator, InvokeMethod invoke);
public abstract void unboxEnums(
AppView<AppInfoWithLiveness> appView,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index acde184..13fc08e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -40,8 +40,10 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues;
import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
@@ -62,7 +64,6 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeCustom;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
@@ -627,6 +628,7 @@
return;
}
+ GraphLens previousLens = appView.graphLens();
ImmutableSet<DexType> enumsToUnbox = enumUnboxingCandidatesInfo.candidates();
ImmutableSet<DexProgramClass> enumClassesToUnbox =
enumUnboxingCandidatesInfo.candidateClasses();
@@ -646,15 +648,19 @@
checkNotNullMethodsBuilder
.rewrittenWithLens(appView, (enumClasses, appliedGraphLens) -> enumClasses)
.build(appView, builder -> builder.build(appView));
+ checkNotNullMethods.removeIf(
+ (checkNotNullMethod, ignore) ->
+ !checkNotNullMethod
+ .getOptimizationInfo()
+ .getEnumUnboxerMethodClassification()
+ .isCheckNotNullClassification());
+
EnumUnboxingTreeFixer.Result treeFixerResult =
new EnumUnboxingTreeFixer(
appView, checkNotNullMethods, enumDataMap, enumClassesToUnbox, utilityClasses)
.fixupTypeReferences(converter, executorService);
EnumUnboxingLens enumUnboxingLens = treeFixerResult.getLens();
- // Update the graph lens.
- appView.rewriteWithLens(enumUnboxingLens);
-
// Enqueue the (lens rewritten) methods that require reprocessing.
//
// Note that the reprocessing set must be rewritten to the new enum unboxing lens before pruning
@@ -674,13 +680,12 @@
.removeAll(treeFixerResult.getPrunedItems().getRemovedMethods()));
methodsDependingOnLibraryModelisation.clear();
- updateOptimizationInfos(executorService, feedback, treeFixerResult);
+ updateOptimizationInfos(executorService, feedback, treeFixerResult, previousLens);
enumUnboxerRewriter =
new EnumUnboxingRewriter(
appView,
treeFixerResult.getCheckNotNullToCheckNotZeroMapping(),
- converter,
enumUnboxingLens,
enumDataMap,
utilityClasses);
@@ -689,8 +694,15 @@
private void updateOptimizationInfos(
ExecutorService executorService,
OptimizationFeedbackDelayed feedback,
- EnumUnboxingTreeFixer.Result treeFixerResult)
+ EnumUnboxingTreeFixer.Result treeFixerResult,
+ GraphLens previousLens)
throws ExecutionException {
+ NonIdentityGraphLens graphLens = appView.graphLens().asNonIdentityLens();
+ assert graphLens.isEnumUnboxerLens();
+
+ GraphLens codeLens = graphLens.getPrevious();
+ assert codeLens == previousLens;
+
feedback.fixupOptimizationInfos(
appView,
executorService,
@@ -698,18 +710,18 @@
@Override
public void fixup(DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo) {
optimizationInfo
- .fixupClassTypeReferences(appView, appView.graphLens())
- .fixupAbstractValue(appView, appView.graphLens());
+ .fixupClassTypeReferences(appView, graphLens)
+ .fixupAbstractValue(appView, graphLens, codeLens);
}
@Override
public void fixup(
DexEncodedMethod method, MutableMethodOptimizationInfo optimizationInfo) {
optimizationInfo
- .fixupClassTypeReferences(appView, appView.graphLens())
- .fixupAbstractReturnValue(appView, appView.graphLens())
+ .fixupClassTypeReferences(appView, graphLens)
+ .fixupAbstractReturnValue(appView, graphLens, codeLens)
.fixupInstanceInitializerInfo(
- appView, appView.graphLens(), treeFixerResult.getPrunedItems());
+ appView, graphLens, codeLens, treeFixerResult.getPrunedItems());
// Clear the enum unboxer method classification for check-not-null methods (these
// classifications are transferred to the synthesized check-not-zero methods by now).
@@ -1448,23 +1460,19 @@
}
@Override
- public Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
+ public Set<Phi> rewriteCode(
+ IRCode code,
+ MethodProcessor methodProcessor,
+ RewrittenPrototypeDescription prototypeChanges) {
// This has no effect during primary processing since the enumUnboxerRewriter is set
// in between primary and post processing.
if (enumUnboxerRewriter != null) {
- return enumUnboxerRewriter.rewriteCode(code, methodProcessor);
+ return enumUnboxerRewriter.rewriteCode(code, methodProcessor, prototypeChanges);
}
return Sets.newIdentityHashSet();
}
@Override
- public void rewriteNullCheck(InstructionListIterator iterator, InvokeMethod invoke) {
- if (enumUnboxerRewriter != null) {
- enumUnboxerRewriter.rewriteNullCheck(iterator, invoke);
- }
- }
-
- @Override
public void unsetRewriter() {
enumUnboxerRewriter = null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index e055f70..5e3b23d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -50,28 +50,34 @@
}
@Override
+ public boolean hasCustomCodeRewritings() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnumUnboxerLens() {
+ return true;
+ }
+
+ @Override
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
// Rewrite the single value of the given RewrittenPrototypeDescription if it is referring to an
// unboxed enum field.
if (prototypeChanges.hasRewrittenReturnInfo()) {
- RewrittenTypeInfo rewrittenTypeInfo = prototypeChanges.getRewrittenReturnInfo();
- if (rewrittenTypeInfo.hasSingleValue()) {
- SingleValue singleValue = rewrittenTypeInfo.getSingleValue();
- if (singleValue.isSingleFieldValue()) {
- SingleFieldValue singleFieldValue = singleValue.asSingleFieldValue();
- if (unboxedEnums.hasUnboxedValueFor(singleFieldValue.getField())) {
- prototypeChanges =
- prototypeChanges.withRewrittenReturnInfo(
- RewrittenTypeInfo.builder()
- .setCastType(rewrittenTypeInfo.getCastType())
- .setOldType(rewrittenTypeInfo.getOldType())
- .setNewType(rewrittenTypeInfo.getNewType())
- .setSingleValue(
- abstractValueFactory.createSingleNumberValue(
- unboxedEnums.getUnboxedValue(singleFieldValue.getField())))
- .build());
- }
+ RewrittenTypeInfo rewrittenReturnInfo = prototypeChanges.getRewrittenReturnInfo();
+ if (rewrittenReturnInfo.hasSingleValue()) {
+ SingleValue singleValue = rewrittenReturnInfo.getSingleValue();
+ SingleValue rewrittenSingleValue = rewriteSingleValue(singleValue);
+ if (rewrittenSingleValue != singleValue) {
+ prototypeChanges =
+ prototypeChanges.withRewrittenReturnInfo(
+ RewrittenTypeInfo.builder()
+ .setCastType(rewrittenReturnInfo.getCastType())
+ .setOldType(rewrittenReturnInfo.getOldType())
+ .setNewType(rewrittenReturnInfo.getNewType())
+ .setSingleValue(rewrittenSingleValue)
+ .build());
}
}
}
@@ -84,6 +90,17 @@
return prototypeChanges.combine(enumUnboxingPrototypeChanges);
}
+ private SingleValue rewriteSingleValue(SingleValue singleValue) {
+ if (singleValue.isSingleFieldValue()) {
+ SingleFieldValue singleFieldValue = singleValue.asSingleFieldValue();
+ if (unboxedEnums.hasUnboxedValueFor(singleFieldValue.getField())) {
+ return abstractValueFactory.createSingleNumberValue(
+ unboxedEnums.getUnboxedValue(singleFieldValue.getField()));
+ }
+ }
+ return singleValue;
+ }
+
@Override
protected Invoke.Type mapInvocationType(
DexMethod newMethod, DexMethod originalMethod, Invoke.Type type) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 787b4da..b0a4ed5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -13,12 +13,16 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.ArrayAccess;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -31,7 +35,6 @@
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
import com.android.tools.r8.ir.optimize.enums.classification.CheckNotNullEnumUnboxerMethodClassification;
@@ -42,6 +45,7 @@
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -51,7 +55,6 @@
private final AppView<AppInfoWithLiveness> appView;
private final Map<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping;
- private final IRConverter converter;
private final DexItemFactory factory;
private final InternalOptions options;
private final EnumDataMap unboxedEnumsData;
@@ -61,13 +64,11 @@
EnumUnboxingRewriter(
AppView<AppInfoWithLiveness> appView,
Map<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping,
- IRConverter converter,
EnumUnboxingLens enumUnboxingLens,
EnumDataMap unboxedEnumsInstanceFieldData,
EnumUnboxingUtilityClasses utilityClasses) {
this.appView = appView;
this.checkNotNullToCheckNotZeroMapping = checkNotNullToCheckNotZeroMapping;
- this.converter = converter;
this.factory = appView.dexItemFactory();
this.options = appView.options();
this.enumUnboxingLens = enumUnboxingLens;
@@ -83,7 +84,36 @@
return utilityClasses.getSharedUtilityClass();
}
- Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
+ private Map<Instruction, DexType> createInitialConvertedEnums(
+ IRCode code, RewrittenPrototypeDescription prototypeChanges) {
+ Map<Instruction, DexType> convertedEnums = new IdentityHashMap<>();
+ Iterator<Instruction> iterator = code.entryBlock().iterator();
+ int originalNumberOfArguments =
+ code.getNumberOfArguments()
+ + prototypeChanges.getArgumentInfoCollection().numberOfRemovedArguments();
+ for (int argumentIndex = 0; argumentIndex < originalNumberOfArguments; argumentIndex++) {
+ ArgumentInfo argumentInfo =
+ prototypeChanges.getArgumentInfoCollection().getArgumentInfo(argumentIndex);
+ if (argumentInfo.isRemovedArgumentInfo()) {
+ continue;
+ }
+ Instruction next = iterator.next();
+ assert next.isArgument();
+ if (argumentInfo.isRewrittenTypeInfo()) {
+ RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
+ DexType enumType = getEnumTypeOrNull(rewrittenTypeInfo.getOldType().toBaseType(factory));
+ if (enumType != null) {
+ convertedEnums.put(next, enumType);
+ }
+ }
+ }
+ return convertedEnums;
+ }
+
+ Set<Phi> rewriteCode(
+ IRCode code,
+ MethodProcessor methodProcessor,
+ RewrittenPrototypeDescription prototypeChanges) {
// We should not process the enum methods, they will be removed and they may contain invalid
// rewriting rules.
if (unboxedEnumsData.isEmpty()) {
@@ -91,7 +121,7 @@
}
assert code.isConsistentSSABeforeTypesAreCorrect();
ProgramMethod context = code.context();
- Map<Instruction, DexType> convertedEnums = new IdentityHashMap<>();
+ Map<Instruction, DexType> convertedEnums = createInitialConvertedEnums(code, prototypeChanges);
Set<Phi> affectedPhis = Sets.newIdentityHashSet();
ListIterator<BasicBlock> blocks = code.listIterator();
Set<BasicBlock> seenBlocks = Sets.newIdentityHashSet();
@@ -109,6 +139,27 @@
continue;
}
+ if (instruction.isIf()) {
+ If ifInstruction = instruction.asIf();
+ if (!ifInstruction.isZeroTest()) {
+ for (int operandIndex = 0; operandIndex < 2; operandIndex++) {
+ Value operand = ifInstruction.getOperand(operandIndex);
+ DexType enumType = getEnumTypeOrNull(operand, convertedEnums);
+ if (enumType != null) {
+ int otherOperandIndex = 1 - operandIndex;
+ Value otherOperand = ifInstruction.getOperand(otherOperandIndex);
+ if (otherOperand.getType().isNullType()) {
+ iterator.previous();
+ ifInstruction.replaceValue(
+ otherOperandIndex, iterator.insertConstIntInstruction(code, options, 0));
+ iterator.next();
+ break;
+ }
+ }
+ }
+ }
+ }
+
// Rewrites specific enum methods, such as ordinal, into their corresponding enum unboxed
// counterpart. The rewriting (== or match) is based on the following:
// - name, ordinal and compareTo are final and implemented only on java.lang.Enum,
@@ -269,14 +320,14 @@
// Rewrite array accesses from MyEnum[] (OBJECT) to int[] (INT).
if (instruction.isArrayAccess()) {
ArrayAccess arrayAccess = instruction.asArrayAccess();
- DexType enumType = getEnumTypeOrNull(arrayAccess);
+ DexType enumType = getEnumTypeOrNull(arrayAccess, convertedEnums);
if (enumType != null) {
if (arrayAccess.hasOutValue()) {
affectedPhis.addAll(arrayAccess.outValue().uniquePhiUsers());
}
- instruction = arrayAccess.withMemberType(MemberType.INT);
- iterator.replaceCurrentInstruction(instruction);
- convertedEnums.put(instruction, enumType);
+ arrayAccess = arrayAccess.withMemberType(MemberType.INT);
+ iterator.replaceCurrentInstruction(arrayAccess);
+ convertedEnums.put(arrayAccess, enumType);
}
assert validateArrayAccess(arrayAccess);
}
@@ -526,8 +577,8 @@
private DexType getEnumTypeOrNull(Value receiver, Map<Instruction, DexType> convertedEnums) {
TypeElement type = receiver.getType();
- if (type.isInt()) {
- return convertedEnums.get(receiver.definition);
+ if (type.isInt() || (type.isArrayType() && type.asArrayType().getBaseType().isInt())) {
+ return receiver.isPhi() ? null : convertedEnums.get(receiver.getDefinition());
}
return getEnumTypeOrNull(type);
}
@@ -536,11 +587,15 @@
if (!type.isClassType()) {
return null;
}
- DexType enumType = type.asClassType().getClassType();
- return unboxedEnumsData.isUnboxedEnum(enumType) ? enumType : null;
+ return getEnumTypeOrNull(type.asClassType().getClassType());
}
- private DexType getEnumTypeOrNull(ArrayAccess arrayAccess) {
+ private DexType getEnumTypeOrNull(DexType type) {
+ return unboxedEnumsData.isUnboxedEnum(type) ? type : null;
+ }
+
+ private DexType getEnumTypeOrNull(
+ ArrayAccess arrayAccess, Map<Instruction, DexType> convertedEnums) {
ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType();
if (arrayType == null) {
assert arrayAccess.array().getType().isNullType();
@@ -550,10 +605,10 @@
return null;
}
TypeElement baseType = arrayType.getBaseType();
- if (!baseType.isClassType()) {
- return null;
+ if (baseType.isClassType()) {
+ DexType classType = baseType.asClassType().getClassType();
+ return unboxedEnumsData.isUnboxedEnum(classType) ? classType : null;
}
- DexType classType = baseType.asClassType().getClassType();
- return unboxedEnumsData.isUnboxedEnum(classType) ? classType : null;
+ return getEnumTypeOrNull(arrayAccess.array(), convertedEnums);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 862840b..773dadf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -132,12 +132,18 @@
}
}
+ // Install the new graph lens before processing any checkNotZero() methods.
+ EnumUnboxingLens lens = lensBuilder.build(appView);
+ appView.rewriteWithLens(lens);
+
+ // Rewrite outliner with lens.
+ converter.outliner.rewriteWithLens();
+
// Create mapping from checkNotNull() to checkNotZero() methods.
BiMap<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping =
duplicateCheckNotNullMethods(converter, executorService);
- return new Result(
- checkNotNullToCheckNotZeroMapping, lensBuilder.build(appView), prunedItemsBuilder.build());
+ return new Result(checkNotNullToCheckNotZeroMapping, lens, prunedItemsBuilder.build());
}
private BiMap<DexMethod, DexMethod> duplicateCheckNotNullMethods(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
index c03d170..3bf8709 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
@@ -64,8 +64,9 @@
this.abstractValue = abstractValue;
}
- public void fixupAbstractValue(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
- setAbstractValue(abstractValue.rewrittenWithLens(appView, lens));
+ public void fixupAbstractValue(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
+ setAbstractValue(abstractValue.rewrittenWithLens(appView, lens, codeLens));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index e07fee5..64bdb91 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -185,15 +185,18 @@
}
public MutableMethodOptimizationInfo fixupAbstractReturnValue(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
- abstractReturnValue = abstractReturnValue.rewrittenWithLens(appView, lens);
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
+ abstractReturnValue = abstractReturnValue.rewrittenWithLens(appView, lens, codeLens);
return this;
}
public MutableMethodOptimizationInfo fixupInstanceInitializerInfo(
- AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
+ AppView<AppInfoWithLiveness> appView,
+ GraphLens lens,
+ GraphLens codeLens,
+ PrunedItems prunedItems) {
instanceInitializerInfoCollection =
- instanceInitializerInfoCollection.rewrittenWithLens(appView, lens, prunedItems);
+ instanceInitializerInfoCollection.rewrittenWithLens(appView, lens, codeLens, prunedItems);
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
index 16a86a4..833f0c6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
@@ -60,7 +60,7 @@
@Override
public InstanceFieldInitializationInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
index 46255f1..b689d8a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
@@ -62,7 +62,7 @@
@Override
public InstanceFieldInitializationInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
// We don't have the context here to determine what should happen. It is the responsibility of
// optimizations that change the proto of instance initializers to update the argument
// initialization info.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
index 94e5ea5..0a34194 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
@@ -51,5 +51,5 @@
ArgumentInfoCollection argumentInfoCollection);
InstanceFieldInitializationInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens);
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
index c5d846e..2520561 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -48,7 +48,7 @@
ArgumentInfoCollection argumentInfoCollection);
public abstract InstanceFieldInitializationInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens);
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens);
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
index 39a72ba..24730bb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -55,7 +55,7 @@
@Override
public InstanceFieldInitializationInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
EnumDataMap enumDataMap = appView.unboxedEnums();
if (dynamicLowerBoundType != null
&& enumDataMap.isUnboxedEnum(dynamicLowerBoundType.getClassType())) {
@@ -69,9 +69,9 @@
}
return new InstanceFieldTypeInitializationInfo(
dynamicLowerBoundType != null
- ? dynamicLowerBoundType.rewrittenWithLens(appView, lens).asClassType()
+ ? dynamicLowerBoundType.rewrittenWithLens(appView, lens, codeLens).asClassType()
: null,
- dynamicUpperBoundType.rewrittenWithLens(appView, lens));
+ dynamicUpperBoundType.rewrittenWithLens(appView, lens, codeLens));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
index 2e34bcd..b996542 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
@@ -80,12 +80,13 @@
@Override
public InstanceFieldInitializationInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
Builder builder = InstanceFieldInitializationInfoCollection.builder();
infos.forEach(
(field, info) ->
builder.recordInitializationInfo(
- lens.lookupField(field), info.rewrittenWithLens(appView, lens)));
+ lens.lookupField(field, codeLens),
+ info.rewrittenWithLens(appView, lens, codeLens)));
return builder.build();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
index f0353a4..77409cb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
@@ -37,7 +37,7 @@
@Override
public InstanceFieldInitializationInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
index 1feccf6..b15e8e4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
@@ -48,9 +48,12 @@
@Override
public ContextInsensitiveInstanceInitializerInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
+ AppView<AppInfoWithLiveness> appView,
+ GraphLens lens,
+ GraphLens codeLens,
+ PrunedItems prunedItems) {
NonTrivialInstanceInitializerInfo rewrittenInfo =
- info.rewrittenWithLens(appView, lens, prunedItems);
+ info.rewrittenWithLens(appView, lens, codeLens, prunedItems);
if (rewrittenInfo != info) {
return new ContextInsensitiveInstanceInitializerInfoCollection(rewrittenInfo);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
index f281892..fec6ed4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
@@ -62,11 +62,14 @@
@Override
public InstanceInitializerInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
+ AppView<AppInfoWithLiveness> appView,
+ GraphLens lens,
+ GraphLens codeLens,
+ PrunedItems prunedItems) {
Builder builder = builder();
infos.forEach(
(context, info) ->
- builder.put(context, info.rewrittenWithLens(appView, lens, prunedItems)));
+ builder.put(context, info.rewrittenWithLens(appView, lens, codeLens, prunedItems)));
return builder.build();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
index 678489b..46a3511 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
@@ -74,7 +74,10 @@
@Override
public InstanceInitializerInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
+ AppView<AppInfoWithLiveness> appView,
+ GraphLens lens,
+ GraphLens codeLens,
+ PrunedItems prunedItems) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
index c3742a5..9ac9762 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
@@ -45,7 +45,10 @@
@Override
public EmptyInstanceInitializerInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
+ AppView<AppInfoWithLiveness> appView,
+ GraphLens lens,
+ GraphLens codeLens,
+ PrunedItems prunedItems) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
index e89b738..e07b514 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
@@ -76,5 +76,8 @@
AppView<AppInfoWithLiveness> appView, ArgumentInfoCollection argumentInfoCollection);
public abstract InstanceInitializerInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems);
+ AppView<AppInfoWithLiveness> appView,
+ GraphLens lens,
+ GraphLens codeLens,
+ PrunedItems prunedItems);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
index f228922..8002513 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
@@ -41,7 +41,10 @@
AppView<AppInfoWithLiveness> appView, ArgumentInfoCollection argumentInfoCollection);
public abstract InstanceInitializerInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems);
+ AppView<AppInfoWithLiveness> appView,
+ GraphLens lens,
+ GraphLens codeLens,
+ PrunedItems prunedItems);
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index 99cb83b..58af519 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -111,12 +111,15 @@
@Override
public NonTrivialInstanceInitializerInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
+ AppView<AppInfoWithLiveness> appView,
+ GraphLens lens,
+ GraphLens codeLens,
+ PrunedItems prunedItems) {
return new NonTrivialInstanceInitializerInfo(
data,
- fieldInitializationInfos.rewrittenWithLens(appView, lens),
- readSet.rewrittenWithLens(appView, lens, prunedItems),
- lens.getRenamedMethodSignature(parent));
+ fieldInitializationInfos.rewrittenWithLens(appView, lens, codeLens),
+ readSet.rewrittenWithLens(appView, lens, codeLens, prunedItems),
+ lens.getRenamedMethodSignature(parent, codeLens));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
index e356796..a82579e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
@@ -32,7 +32,6 @@
public class EmulateDispatchSyntheticCfCodeProvider extends SyntheticCfCodeProvider {
private final DexMethod forwardingMethod;
- private final DexType receiverType;
private final DexMethod interfaceMethod;
private final LinkedHashMap<DexType, DexMethod> extraDispatchCases;
@@ -44,13 +43,13 @@
AppView<?> appView) {
super(appView, holder);
this.forwardingMethod = forwardingMethod;
- this.receiverType = forwardingMethod.getParameter(0);
this.interfaceMethod = interfaceMethod;
this.extraDispatchCases = extraDispatchCases;
}
@Override
public CfCode generateCfCode() {
+ DexType receiverType = forwardingMethod.getParameter(0);
List<CfInstruction> instructions = new ArrayList<>();
CfLabel[] labels = new CfLabel[extraDispatchCases.size() + 1];
for (int i = 0; i < labels.length; i++) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
index f2a6eb4..e2a2083 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
@@ -8,39 +8,23 @@
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
import com.android.tools.r8.utils.Reporter;
-import java.util.List;
import kotlinx.metadata.KmFlexibleTypeUpperBound;
-import kotlinx.metadata.KmType;
-import kotlinx.metadata.jvm.JvmExtensionsKt;
-public class KotlinFlexibleTypeUpperBoundInfo extends KotlinTypeInfo {
+public class KotlinFlexibleTypeUpperBoundInfo implements EnqueuerMetadataTraceable {
private static final String KOTLIN_JVM_PLATFORMTYPE = "kotlin.jvm.PlatformType";
private static final KotlinFlexibleTypeUpperBoundInfo NO_FLEXIBLE_UPPER_BOUND =
- new KotlinFlexibleTypeUpperBoundInfo(
- 0, null, null, null, null, null, null, KOTLIN_JVM_PLATFORMTYPE);
+ new KotlinFlexibleTypeUpperBoundInfo(KOTLIN_JVM_PLATFORMTYPE, null);
private final String typeFlexibilityId;
+ private final KotlinTypeInfo kotlinTypeInfo;
private KotlinFlexibleTypeUpperBoundInfo(
- int flags,
- KotlinClassifierInfo classifier,
- KotlinTypeInfo abbreviatedType,
- KotlinTypeInfo outerType,
- List<KotlinTypeProjectionInfo> arguments,
- List<KotlinAnnotationInfo> annotations,
- KotlinFlexibleTypeUpperBoundInfo flexibleTypeUpperBoundInfo,
- String typeFlexibilityId) {
- super(
- flags,
- classifier,
- abbreviatedType,
- outerType,
- arguments,
- annotations,
- flexibleTypeUpperBoundInfo);
+ String typeFlexibilityId, KotlinTypeInfo kotlinTypeInfo) {
this.typeFlexibilityId = typeFlexibilityId;
+ this.kotlinTypeInfo = kotlinTypeInfo;
assert KOTLIN_JVM_PLATFORMTYPE.equals(typeFlexibilityId);
}
@@ -49,17 +33,9 @@
if (flexibleTypeUpperBound == null) {
return NO_FLEXIBLE_UPPER_BOUND;
}
- KmType kmType = flexibleTypeUpperBound.getType();
return new KotlinFlexibleTypeUpperBoundInfo(
- kmType.getFlags(),
- KotlinClassifierInfo.create(kmType.classifier, factory, reporter),
- KotlinTypeInfo.create(kmType.getAbbreviatedType(), factory, reporter),
- KotlinTypeInfo.create(kmType.getOuterType(), factory, reporter),
- getArguments(kmType.getArguments(), factory, reporter),
- KotlinAnnotationInfo.create(JvmExtensionsKt.getAnnotations(kmType), factory),
- KotlinFlexibleTypeUpperBoundInfo.create(
- kmType.getFlexibleTypeUpperBound(), factory, reporter),
- flexibleTypeUpperBound.getTypeFlexibilityId());
+ flexibleTypeUpperBound.getTypeFlexibilityId(),
+ KotlinTypeInfo.create(flexibleTypeUpperBound.getType(), factory, reporter));
}
boolean rewrite(
@@ -70,7 +46,11 @@
// Nothing to do.
return false;
}
- return super.rewrite(
+ if (kotlinTypeInfo == null) {
+ assert false;
+ return false;
+ }
+ return kotlinTypeInfo.rewrite(
flags -> visitorProvider.get(flags, typeFlexibilityId), appView, namingLens);
}
@@ -79,6 +59,10 @@
if (this == NO_FLEXIBLE_UPPER_BOUND) {
return;
}
- super.trace(definitionSupplier);
+ if (kotlinTypeInfo == null) {
+ assert false;
+ return;
+ }
+ kotlinTypeInfo.trace(definitionSupplier);
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
index 9503404..6127754 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -34,6 +34,7 @@
private final List<KotlinTypeProjectionInfo> arguments;
private final List<KotlinAnnotationInfo> annotations;
private final KotlinFlexibleTypeUpperBoundInfo flexibleTypeUpperBound;
+ private final boolean isRaw;
KotlinTypeInfo(
int flags,
@@ -42,7 +43,8 @@
KotlinTypeInfo outerType,
List<KotlinTypeProjectionInfo> arguments,
List<KotlinAnnotationInfo> annotations,
- KotlinFlexibleTypeUpperBoundInfo flexibleTypeUpperBound) {
+ KotlinFlexibleTypeUpperBoundInfo flexibleTypeUpperBound,
+ boolean isRaw) {
this.flags = flags;
this.classifier = classifier;
this.abbreviatedType = abbreviatedType;
@@ -50,6 +52,7 @@
this.arguments = arguments;
this.annotations = annotations;
this.flexibleTypeUpperBound = flexibleTypeUpperBound;
+ this.isRaw = isRaw;
}
static KotlinTypeInfo create(KmType kmType, DexItemFactory factory, Reporter reporter) {
@@ -64,7 +67,8 @@
getArguments(kmType.getArguments(), factory, reporter),
KotlinAnnotationInfo.create(JvmExtensionsKt.getAnnotations(kmType), factory),
KotlinFlexibleTypeUpperBoundInfo.create(
- kmType.getFlexibleTypeUpperBound(), factory, reporter));
+ kmType.getFlexibleTypeUpperBound(), factory, reporter),
+ JvmExtensionsKt.isRaw(kmType));
}
static List<KotlinTypeProjectionInfo> getArguments(
@@ -104,7 +108,7 @@
rewritten |=
flexibleTypeUpperBound.rewrite(
kmTypeVisitor::visitFlexibleTypeUpperBound, appView, namingLens);
- if (annotations.isEmpty()) {
+ if (annotations.isEmpty() && !isRaw) {
return rewritten;
}
JvmTypeExtensionVisitor extensionVisitor =
@@ -113,6 +117,7 @@
for (KotlinAnnotationInfo annotation : annotations) {
rewritten |= annotation.rewrite(extensionVisitor::visitAnnotation, appView, namingLens);
}
+ extensionVisitor.visit(isRaw);
}
return rewritten;
}
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 8a8d804..b2b9e9f 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -272,6 +272,11 @@
}
private void reserveNamesInClasses() {
+ // Ensure reservation state for java.lang.Object is always created, even if the type is missing.
+ allocateReservationStateAndReserve(
+ appView.dexItemFactory().objectType,
+ appView.dexItemFactory().objectType,
+ rootReservationState);
TopDownClassHierarchyTraversal.forAllClasses(appView)
.visit(
appView.appInfo().classes(),
@@ -336,6 +341,10 @@
if (reservationState != null) {
return reservationState;
}
+ if (appView.definitionFor(type) == null) {
+ // Reservation states for missing definitions is always object.
+ return reservationStates.get(appView.dexItemFactory().objectType);
+ }
// If we cannot find the reservation state, which is a result from a library class extending
// a program class. The gap is tracked in the frontier state.
assert frontiers.containsKey(type);
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
index 4f56738..f4b80a7 100644
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -71,7 +71,10 @@
}
@Override
- public DexField getRenamedFieldSignature(DexField originalField) {
+ public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
+ if (this == codeLens) {
+ return originalField;
+ }
return getPrevious().getRenamedFieldSignature(originalField);
}
@@ -92,13 +95,17 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
return method;
}
@Override
- public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
- return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+ DexMethod method, GraphLens codeLens) {
+ if (this == codeLens) {
+ return getIdentityLens().lookupPrototypeChangesForMethodDefinition(method, codeLens);
+ }
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method, codeLens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 32bc033..25667ea 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -73,6 +73,25 @@
return original;
}
+ if (invokeType.isSuper() && options.canHaveSuperInvokeBug()) {
+ // To preserve semantics we should find the first library method on the boundary.
+ DexClass libraryHolder =
+ appView.definitionFor(
+ firstLibraryClassOrFirstInterfaceTarget(
+ resolutionResult.getResolvedHolder(),
+ appView,
+ resolvedMethod.getReference(),
+ original.getHolderType(),
+ DexClass::lookupMethod));
+ if (libraryHolder == null) {
+ return original;
+ }
+ if (libraryHolder == resolvedMethod.getHolder()) {
+ return resolvedMethod.getReference();
+ }
+ return resolvedMethod.getReference().withHolder(libraryHolder, appView.dexItemFactory());
+ }
+
LibraryMethod eligibleLibraryMethod = null;
SingleResolutionResult currentResolutionResult = resolutionResult;
while (currentResolutionResult != null) {
@@ -106,13 +125,12 @@
}
DexType newHolder =
- resolvedMethod.getHolder().isInterface()
- ? firstLibraryClassForInterfaceTarget(
- appView,
- resolvedMethod.getReference(),
- original.getHolderType(),
- DexClass::lookupMethod)
- : firstLibraryClass(appView, original.getHolderType());
+ firstLibraryClassOrFirstInterfaceTarget(
+ resolvedMethod.getHolder(),
+ appView,
+ resolvedMethod.getReference(),
+ original.getHolderType(),
+ DexClass::lookupMethod);
return newHolder != null ? original.withHolder(newHolder, appView.dexItemFactory()) : original;
}
@@ -159,15 +177,28 @@
}
DexClass fieldHolder = field.getHolder();
DexType newHolder =
- fieldHolder.isInterface()
- ? firstLibraryClassForInterfaceTarget(
- definitions, field.getReference(), original.getHolderType(), DexClass::lookupField)
- : firstLibraryClass(definitions, original.getHolderType());
+ firstLibraryClassOrFirstInterfaceTarget(
+ fieldHolder,
+ definitions,
+ field.getReference(),
+ original.getHolderType(),
+ DexClass::lookupField);
return newHolder != null
? original.withHolder(newHolder, definitions.dexItemFactory())
: original;
}
+ private static <T> DexType firstLibraryClassOrFirstInterfaceTarget(
+ DexClass holder,
+ DexDefinitionSupplier definitions,
+ T target,
+ DexType current,
+ BiFunction<DexClass, T, ?> lookup) {
+ return holder.isInterface()
+ ? firstLibraryClassForInterfaceTarget(definitions, target, current, lookup)
+ : firstLibraryClass(definitions, current);
+ }
+
private static <T> DexType firstLibraryClassForInterfaceTarget(
DexDefinitionSupplier definitions,
T target,
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 8d8962e..81c4ae1 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -100,7 +100,10 @@
}
@Override
- public DexField getRenamedFieldSignature(DexField originalField) {
+ public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
+ if (this == codeLens) {
+ return originalField;
+ }
return getPrevious().getRenamedFieldSignature(originalField);
}
@@ -115,13 +118,17 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
return method;
}
@Override
- public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
- return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+ DexMethod method, GraphLens codeLens) {
+ if (this == codeLens) {
+ return getIdentityLens().lookupPrototypeChangesForMethodDefinition(method, codeLens);
+ }
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method, codeLens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index e17606f..da5a6d5 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -68,7 +68,10 @@
}
@Override
- public DexField getRenamedFieldSignature(DexField originalField) {
+ public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
+ if (this == codeLens) {
+ return originalField;
+ }
return getPrevious().getRenamedFieldSignature(originalField);
}
@@ -80,8 +83,12 @@
}
@Override
- public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
- return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+ DexMethod method, GraphLens codeLens) {
+ if (this == codeLens) {
+ return getIdentityLens().lookupPrototypeChangesForMethodDefinition(method, codeLens);
+ }
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method, codeLens);
}
@Override
@@ -127,7 +134,7 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
return method;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index 5f43dd3..4b8bff6 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -206,7 +206,6 @@
appView,
immediateSubtypingInfo,
codeScannerResult,
- reprocessingCriteriaCollection,
stronglyConnectedProgramComponents,
interfaceDispatchOutsideProgram)
.populateOptimizationInfo(converter, executorService, timing);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index d8757d1..85bef2b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -14,6 +14,9 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.MethodCollection;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
@@ -75,8 +78,14 @@
}
private void fixupFields(DexProgramClass clazz) {
- clazz.setInstanceFields(fixupFields(clazz.instanceFields()));
- clazz.setStaticFields(fixupFields(clazz.staticFields()));
+ clazz.setInstanceFields(
+ fixupFields(
+ clazz.instanceFields(),
+ builder -> builder.setGenericSignature(FieldTypeSignature.noSignature())));
+ clazz.setStaticFields(
+ fixupFields(
+ clazz.staticFields(),
+ builder -> builder.setGenericSignature(FieldTypeSignature.noSignature())));
}
private void fixupMethods(DexProgramClass clazz) {
@@ -97,8 +106,9 @@
if (graphLens.hasPrototypeChanges(methodReferenceAfterParameterRemoval)) {
RewrittenPrototypeDescription prototypeChanges =
graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
- builder.apply(prototypeChanges.createParameterAnnotationsRemover(method));
-
+ builder
+ .apply(prototypeChanges.createParameterAnnotationsRemover(method))
+ .setGenericSignature(MethodTypeSignature.noSignature());
if (method.isInstance()
&& prototypeChanges.getArgumentInfoCollection().isArgumentRemoved(0)) {
builder
@@ -111,6 +121,7 @@
}
private void fixupOptimizationInfos(ExecutorService executorService) throws ExecutionException {
+ GraphLens codeLens = graphLens.getPrevious();
PrunedItems prunedItems = PrunedItems.empty(appView.app());
getSimpleFeedback()
.fixupOptimizationInfos(
@@ -120,7 +131,7 @@
@Override
public void fixup(
DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo) {
- optimizationInfo.fixupAbstractValue(appView, graphLens);
+ optimizationInfo.fixupAbstractValue(appView, graphLens, codeLens);
}
@Override
@@ -129,8 +140,8 @@
// Fixup the return value in case the method returns a field that had its signature
// changed.
optimizationInfo
- .fixupAbstractReturnValue(appView, graphLens)
- .fixupInstanceInitializerInfo(appView, graphLens, prunedItems);
+ .fixupAbstractReturnValue(appView, graphLens, codeLens)
+ .fixupInstanceInitializerInfo(appView, graphLens, codeLens, prunedItems);
// Rewrite the optimization info to account for method signature changes.
if (graphLens.hasPrototypeChanges(method.getReference())) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index 0955d0a..866fdd3 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -45,7 +45,7 @@
}
public boolean isAffected(DexMethod method) {
- return method != internalGetPreviousMethodSignature(method) || hasPrototypeChanges(method);
+ return method != getPreviousMethodSignature(method) || hasPrototypeChanges(method);
}
@Override
@@ -70,7 +70,7 @@
@Override
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
- DexMethod previous = internalGetPreviousMethodSignature(method);
+ DexMethod previous = getPreviousMethodSignature(method);
if (!hasPrototypeChanges(method)) {
return prototypeChanges;
}
@@ -83,8 +83,8 @@
}
@Override
- public DexMethod internalGetPreviousMethodSignature(DexMethod method) {
- return super.internalGetPreviousMethodSignature(method);
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
+ return super.getPreviousMethodSignature(method);
}
@Override
@@ -177,7 +177,8 @@
for (Entry<DexMethod, RewrittenPrototypeDescription> entry : prototypeChanges.entrySet()) {
RewrittenPrototypeDescription prototypeChangesForMethod = entry.getValue();
RewrittenPrototypeDescription rewrittenPrototypeChangesForMethod =
- prototypeChangesForMethod.rewrittenWithLens(appView, argumentPropagatorGraphLens);
+ prototypeChangesForMethod.rewrittenWithLens(
+ appView, argumentPropagatorGraphLens, argumentPropagatorGraphLens.getPrevious());
if (rewrittenPrototypeChangesForMethod != prototypeChangesForMethod) {
entry.setValue(rewrittenPrototypeChangesForMethod);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index f9bfde1..f8d617b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -13,19 +13,16 @@
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.DynamicType;
-import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeParameterState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeParameterState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeParameterState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
@@ -33,14 +30,11 @@
import com.android.tools.r8.optimize.argumentpropagation.propagation.InParameterFlowPropagator;
import com.android.tools.r8.optimize.argumentpropagation.propagation.InterfaceMethodArgumentPropagator;
import com.android.tools.r8.optimize.argumentpropagation.propagation.VirtualDispatchMethodArgumentPropagator;
-import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection;
import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.Iterables;
-import java.util.BitSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -56,7 +50,6 @@
private final AppView<AppInfoWithLiveness> appView;
private final MethodStateCollectionByReference methodStates;
- private final ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection;
private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
private final List<Set<DexProgramClass>> stronglyConnectedProgramComponents;
@@ -68,13 +61,11 @@
AppView<AppInfoWithLiveness> appView,
ImmediateProgramSubtypingInfo immediateSubtypingInfo,
MethodStateCollectionByReference methodStates,
- ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection,
List<Set<DexProgramClass>> stronglyConnectedProgramComponents,
BiConsumer<Set<DexProgramClass>, DexMethodSignature> interfaceDispatchOutsideProgram) {
this.appView = appView;
this.immediateSubtypingInfo = immediateSubtypingInfo;
this.methodStates = methodStates;
- this.reprocessingCriteriaCollection = reprocessingCriteriaCollection;
this.stronglyConnectedProgramComponents = stronglyConnectedProgramComponents;
this.interfaceDispatchOutsideProgram = interfaceDispatchOutsideProgram;
}
@@ -172,7 +163,6 @@
}
methodState = getMethodStateAfterUninstantiatedParameterRemoval(method, methodState);
- methodState = getMethodStateAfterUnusedParameterRemoval(method, methodState);
if (methodState.isUnknown()) {
// Nothing is known about the arguments to this method.
@@ -254,57 +244,6 @@
: methodState;
}
- private MethodState getMethodStateAfterUnusedParameterRemoval(
- ProgramMethod method, MethodState methodState) {
- assert methodState.isMonomorphic() || methodState.isUnknown();
- if (!method.getOptimizationInfo().hasUnusedArguments()
- || appView.appInfo().isKeepUnusedArgumentsMethod(method)) {
- return methodState;
- }
-
- int numberOfArguments = method.getDefinition().getNumberOfArguments();
- List<ParameterState> parameterStates =
- methodState.isMonomorphic()
- ? methodState.asMonomorphic().getParameterStates()
- : ListUtils.newInitializedArrayList(numberOfArguments, ParameterState.unknown());
-
- BitSet unusedArguments = method.getOptimizationInfo().getUnusedArguments();
- for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
- argumentIndex < numberOfArguments;
- argumentIndex++) {
- boolean isUnused = unusedArguments.get(argumentIndex);
- if (isUnused) {
- DexType argumentType = method.getArgumentType(argumentIndex);
- parameterStates.set(argumentIndex, getUnusedParameterState(argumentType));
- }
- }
-
- if (methodState.isUnknown()) {
- if (!unusedArguments.get(0) || Iterables.any(parameterStates, ParameterState::isConcrete)) {
- assert parameterStates.stream().anyMatch(ParameterState::isConcrete);
- return new ConcreteMonomorphicMethodState(parameterStates);
- }
- }
- return methodState;
- }
-
- private ParameterState getUnusedParameterState(DexType argumentType) {
- if (argumentType.isArrayType()) {
- // Ensure argument removal by simulating that this unused parameter is the constant null.
- return new ConcreteArrayTypeParameterState(Nullability.definitelyNull());
- } else if (argumentType.isClassType()) {
- // Ensure argument removal by simulating that this unused parameter is the constant null.
- return new ConcreteClassTypeParameterState(
- appView.abstractValueFactory().createNullValue(), DynamicType.definitelyNull());
- } else {
- assert argumentType.isPrimitiveType();
- // Ensure argument removal by simulating that this unused parameter is the constant zero.
- // Note that the same zero value is used for all primitive types.
- return new ConcretePrimitiveTypeParameterState(
- appView.abstractValueFactory().createZeroValue());
- }
- }
-
private boolean widenDynamicTypes(
ProgramMethod method, ConcreteMonomorphicMethodState methodState) {
for (int argumentIndex = 0;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 0e68d70..c2912c0 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -29,22 +30,27 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorGraphLens.Builder;
import com.android.tools.r8.optimize.argumentpropagation.utils.ParameterRemovalUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepFieldInfo;
+import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.AccessUtils;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
import com.android.tools.r8.utils.collections.ProgramMethodMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -160,11 +166,12 @@
Timing timing)
throws ExecutionException {
timing.begin("Optimize components");
+ ProcessorContext processorContext = appView.createProcessorContext();
Collection<Builder> partialGraphLensBuilders =
ThreadUtils.processItemsWithResults(
stronglyConnectedProgramComponents,
classes ->
- new StronglyConnectedComponentOptimizer()
+ new StronglyConnectedComponentOptimizer(processorContext)
.optimize(
classes,
interfaceDispatchOutsideProgram.getOrDefault(
@@ -208,8 +215,10 @@
public class StronglyConnectedComponentOptimizer {
- private final DexItemFactory dexItemFactory;
- private final InternalOptions options;
+ private final DexItemFactory dexItemFactory = appView.dexItemFactory();
+ private final InternalOptions options = appView.options();
+ private final CallSiteOptimizationOptions callSiteOptimizationOptions =
+ appView.options().callSiteOptimizationOptions();
private final Map<DexMethodSignature, AllowedPrototypeChanges>
allowedPrototypeChangesForVirtualMethods = new HashMap<>();
@@ -231,9 +240,10 @@
private final Map<DexMethodSignature, Pair<AllowedPrototypeChanges, DexMethodSignature>>
occupiedMethodSignatures = new HashMap<>();
- public StronglyConnectedComponentOptimizer() {
- this.dexItemFactory = appView.dexItemFactory();
- this.options = appView.options();
+ private final ProcessorContext processorContext;
+
+ public StronglyConnectedComponentOptimizer(ProcessorContext processorContext) {
+ this.processorContext = processorContext;
}
// TODO(b/190154391): Strengthen the static type of parameters.
@@ -287,7 +297,9 @@
clazz.forEachProgramMethodMatching(
method -> !method.isInstanceInitializer(),
method -> {
- if (!appView.getKeepInfo(method).isShrinkingAllowed(options)) {
+ KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+ if (!keepInfo.isOptimizationAllowed(options)
+ || !keepInfo.isShrinkingAllowed(options)) {
pinnedMethodSignatures.add(method.getMethodSignature());
}
});
@@ -463,7 +475,7 @@
}
ProgramMethod method = methods.getFirst();
return method.getOptimizationInfo().hasUnusedArguments()
- && method.getOptimizationInfo().getUnusedArguments().get(0)
+ && method.getOptimizationInfo().getUnusedArguments().get(parameterIndex)
&& ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
&& ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, parameterIndex);
}
@@ -472,6 +484,13 @@
// OK, this parameter can be removed.
continue;
}
+ if (method.getOptimizationInfo().hasUnusedArguments()
+ && method.getOptimizationInfo().getUnusedArguments().get(parameterIndex)
+ && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
+ && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, parameterIndex)) {
+ // OK, this parameter is unused.
+ continue;
+ }
CallSiteOptimizationInfo optimizationInfo = method.getOptimizationInfo().getArgumentInfos();
if (optimizationInfo.isConcreteCallSiteOptimizationInfo()) {
ConcreteCallSiteOptimizationInfo concreteOptimizationInfo =
@@ -550,14 +569,39 @@
}
});
DexMethodSignatureSet instanceInitializerSignatures = DexMethodSignatureSet.create();
- clazz.forEachProgramInstanceInitializer(instanceInitializerSignatures::add);
+ ProgramMethodMap<RewrittenPrototypeDescription> instanceInitializerPrototypeChanges =
+ ProgramMethodMap.create();
+ clazz.forEachProgramInstanceInitializer(
+ method -> {
+ RewrittenPrototypeDescription prototypeChanges =
+ computePrototypeChangesForDirectMethod(
+ method, interfaceDispatchOutsideProgram, null);
+ if (prototypeChanges.isEmpty()) {
+ instanceInitializerSignatures.add(method);
+ }
+ instanceInitializerPrototypeChanges.put(method, prototypeChanges);
+ });
clazz.forEachProgramMethod(
method -> {
RewrittenPrototypeDescription prototypeChanges =
- method.getDefinition().belongsToDirectPool()
- ? computePrototypeChangesForDirectMethod(
- method, interfaceDispatchOutsideProgram, instanceInitializerSignatures)
- : computePrototypeChangesForVirtualMethod(method);
+ instanceInitializerPrototypeChanges.getOrDefault(
+ method,
+ () ->
+ method.getDefinition().belongsToDirectPool()
+ ? computePrototypeChangesForDirectMethod(
+ method,
+ interfaceDispatchOutsideProgram,
+ instanceInitializerSignatures)
+ : computePrototypeChangesForVirtualMethod(method));
+ if (method.getDefinition().isInstanceInitializer()) {
+ if (prototypeChanges.isEmpty()) {
+ assert instanceInitializerSignatures.contains(method);
+ return;
+ }
+ prototypeChanges =
+ selectInitArgumentTypeForInstanceInitializer(
+ method, prototypeChanges, instanceInitializerSignatures);
+ }
DexMethod newMethodSignature = getNewMethodSignature(method, prototypeChanges);
if (newMethodSignature != method.getReference()) {
partialGraphLensBuilder.recordMove(
@@ -722,31 +766,54 @@
DexMethodSignatureSet interfaceDispatchOutsideProgram,
DexMethodSignatureSet instanceInitializerSignatures) {
assert method.getDefinition().belongsToDirectPool();
- if (!isPrototypeChangesAllowed(method, interfaceDispatchOutsideProgram)) {
- return RewrittenPrototypeDescription.none();
- }
- // TODO(b/199864962): Allow parameter removal from check-not-null classified methods.
- if (method
- .getOptimizationInfo()
- .getEnumUnboxerMethodClassification()
- .isCheckNotNullClassification()) {
- return RewrittenPrototypeDescription.none();
- }
- RewrittenPrototypeDescription prototypeChanges = computePrototypeChangesForMethod(method);
- if (prototypeChanges.isEmpty()) {
- return prototypeChanges;
- }
- if (method.getDefinition().isInstanceInitializer()) {
- DexMethod rewrittenMethod =
- prototypeChanges.getArgumentInfoCollection().rewriteMethod(method, dexItemFactory);
- assert rewrittenMethod != method.getReference();
- if (!instanceInitializerSignatures.add(rewrittenMethod)) {
- return RewrittenPrototypeDescription.none();
- }
+ RewrittenPrototypeDescription prototypeChanges =
+ isPrototypeChangesAllowed(method, interfaceDispatchOutsideProgram)
+ ? computePrototypeChangesForMethod(method)
+ : RewrittenPrototypeDescription.none();
+ if (method.getDefinition().isInstanceInitializer() && instanceInitializerSignatures != null) {
+ prototypeChanges =
+ selectInitArgumentTypeForInstanceInitializer(
+ method, prototypeChanges, instanceInitializerSignatures);
}
return prototypeChanges;
}
+ private RewrittenPrototypeDescription selectInitArgumentTypeForInstanceInitializer(
+ ProgramMethod method,
+ RewrittenPrototypeDescription prototypeChanges,
+ DexMethodSignatureSet instanceInitializerSignatures) {
+ DexMethod rewrittenMethod = prototypeChanges.rewriteMethod(method, dexItemFactory);
+ if (instanceInitializerSignatures.add(rewrittenMethod)) {
+ return prototypeChanges;
+ }
+ if (!callSiteOptimizationOptions.isForceSyntheticsForInstanceInitializersEnabled()) {
+ for (DexType extraArgumentType :
+ ImmutableList.of(dexItemFactory.intType, dexItemFactory.objectType)) {
+ RewrittenPrototypeDescription candidatePrototypeChanges =
+ prototypeChanges.withExtraParameters(new ExtraUnusedNullParameter(extraArgumentType));
+ rewrittenMethod = candidatePrototypeChanges.rewriteMethod(method, dexItemFactory);
+ if (instanceInitializerSignatures.add(rewrittenMethod)) {
+ return candidatePrototypeChanges;
+ }
+ }
+ }
+ DexType extraArgumentType =
+ appView
+ .getSyntheticItems()
+ .createClass(
+ SyntheticKind.NON_FIXED_INIT_TYPE_ARGUMENT,
+ processorContext.createMethodProcessingContext(method).createUniqueContext(),
+ appView)
+ .getType();
+ RewrittenPrototypeDescription finalPrototypeChanges =
+ prototypeChanges.withExtraParameters(new ExtraUnusedNullParameter(extraArgumentType));
+ boolean added =
+ instanceInitializerSignatures.add(
+ finalPrototypeChanges.rewriteMethod(method, dexItemFactory));
+ assert added;
+ return finalPrototypeChanges;
+ }
+
private RewrittenPrototypeDescription computePrototypeChangesForVirtualMethod(
ProgramMethod method) {
AllowedPrototypeChanges allowedPrototypeChanges =
@@ -933,41 +1000,54 @@
&& ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
&& ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, 0)) {
parameterChangesBuilder.addArgumentInfo(
- 0, RemovedArgumentInfo.builder().setType(method.getHolderType()).build());
+ 0,
+ RemovedArgumentInfo.builder()
+ .setCheckNullOrZero(true)
+ .setType(method.getHolderType())
+ .build());
}
- ConcreteCallSiteOptimizationInfo optimizationInfo =
- method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
- if (optimizationInfo != null) {
- for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
- argumentIndex < method.getDefinition().getNumberOfArguments();
- argumentIndex++) {
- if (removableParameterIndices.test(argumentIndex)) {
- AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
- if (abstractValue.isSingleValue()
- && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
- parameterChangesBuilder.addArgumentInfo(
- argumentIndex,
- RemovedArgumentInfo.builder()
- .setSingleValue(abstractValue.asSingleValue())
- .setType(method.getArgumentType(argumentIndex))
- .build());
- continue;
- }
- }
-
- DexType dynamicType = newParameterTypes.apply(argumentIndex);
- if (dynamicType != null) {
- DexType staticType = method.getArgumentType(argumentIndex);
- assert dynamicType != staticType;
+ CallSiteOptimizationInfo optimizationInfo = method.getOptimizationInfo().getArgumentInfos();
+ for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
+ argumentIndex < method.getDefinition().getNumberOfArguments();
+ argumentIndex++) {
+ if (removableParameterIndices.test(argumentIndex)) {
+ if (method.getOptimizationInfo().hasUnusedArguments()
+ && method.getOptimizationInfo().getUnusedArguments().get(argumentIndex)
+ && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
+ && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, argumentIndex)) {
parameterChangesBuilder.addArgumentInfo(
argumentIndex,
- RewrittenTypeInfo.builder()
- .setCastType(dynamicType)
- .setOldType(staticType)
- .setNewType(dynamicType)
+ RemovedArgumentInfo.builder()
+ .setType(method.getArgumentType(argumentIndex))
.build());
+ continue;
}
+
+ AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
+ if (abstractValue.isSingleValue()
+ && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
+ parameterChangesBuilder.addArgumentInfo(
+ argumentIndex,
+ RemovedArgumentInfo.builder()
+ .setSingleValue(abstractValue.asSingleValue())
+ .setType(method.getArgumentType(argumentIndex))
+ .build());
+ continue;
+ }
+ }
+
+ DexType dynamicType = newParameterTypes.apply(argumentIndex);
+ if (dynamicType != null) {
+ DexType staticType = method.getArgumentType(argumentIndex);
+ assert dynamicType != staticType;
+ parameterChangesBuilder.addArgumentInfo(
+ argumentIndex,
+ RewrittenTypeInfo.builder()
+ .setCastType(dynamicType)
+ .setOldType(staticType)
+ .setNewType(dynamicType)
+ .build());
}
}
return parameterChangesBuilder.build();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
index 0d16b37..72cbfba 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
@@ -19,6 +19,9 @@
if (!keepInfo.isParameterRemovalAllowed(options)) {
return false;
}
+ if (appView.appInfo().isKeepUnusedArgumentsMethod(method)) {
+ return false;
+ }
return !appView.appInfoWithLiveness().isMethodTargetedByInvokeDynamic(method);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
index c387c1c..1ded13b 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
@@ -33,7 +33,7 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
Set<DexMethod> bridges = bridgeToHoistedBridgeMap.getKeys(method);
return bridges.isEmpty() ? method : bridges.iterator().next();
}
@@ -54,13 +54,20 @@
}
@Override
- public DexField getRenamedFieldSignature(DexField originalField) {
+ public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
+ if (this == codeLens) {
+ return originalField;
+ }
return getPrevious().getRenamedFieldSignature(originalField);
}
@Override
- public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
- return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+ DexMethod method, GraphLens codeLens) {
+ if (this == codeLens) {
+ return getIdentityLens().lookupPrototypeChangesForMethodDefinition(method, codeLens);
+ }
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method, codeLens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index d38df4c..71d4807 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1632,7 +1632,7 @@
}
public SubtypingInfo computeSubtypingInfo() {
- return new SubtypingInfo(this);
+ return SubtypingInfo.create(this);
}
public boolean mayHaveFinalizeMethodDirectlyOrIndirectly(ClassTypeElement type) {
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 68dba3e..33b077d 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -608,16 +608,17 @@
}
private void recordTypeReference(DexType type, ProgramDefinition context) {
- recordTypeReference(type, context, this::reportMissingClass);
+ recordTypeReference(type, context, this::recordNonProgramClass, this::reportMissingClass);
}
private void recordTypeReference(DexType type, ProgramDerivedContext context) {
- recordTypeReference(type, context, this::reportMissingClass);
+ recordTypeReference(type, context, this::recordNonProgramClass, this::reportMissingClass);
}
private void recordTypeReference(
DexType type,
ProgramDerivedContext context,
+ BiConsumer<DexClass, ProgramDerivedContext> foundClassConsumer,
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
if (type == null) {
return;
@@ -629,21 +630,22 @@
return;
}
// Lookup the definition, ignoring the result. This populates the missing and referenced sets.
- definitionFor(type, context, missingClassConsumer);
+ definitionFor(type, context, foundClassConsumer, missingClassConsumer);
}
private void recordMethodReference(DexMethod method, ProgramDerivedContext context) {
- recordMethodReference(method, context, this::reportMissingClass);
+ recordMethodReference(method, context, this::recordNonProgramClass, this::reportMissingClass);
}
private void recordMethodReference(
DexMethod method,
ProgramDerivedContext context,
+ BiConsumer<DexClass, ProgramDerivedContext> foundClassConsumer,
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
- recordTypeReference(method.holder, context, missingClassConsumer);
- recordTypeReference(method.proto.returnType, context, missingClassConsumer);
+ recordTypeReference(method.holder, context, foundClassConsumer, missingClassConsumer);
+ recordTypeReference(method.proto.returnType, context, foundClassConsumer, missingClassConsumer);
for (DexType type : method.proto.parameters.values) {
- recordTypeReference(type, context, missingClassConsumer);
+ recordTypeReference(type, context, foundClassConsumer, missingClassConsumer);
}
}
@@ -661,31 +663,28 @@
}
public DexClass definitionFor(DexType type, ProgramDefinition context) {
- return definitionFor(type, context, this::reportMissingClass);
+ return definitionFor(type, context, this::recordNonProgramClass, this::reportMissingClass);
}
private DexClass definitionFor(
DexType type,
ProgramDerivedContext context,
+ BiConsumer<DexClass, ProgramDerivedContext> foundClassConsumer,
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
- return internalDefinitionFor(type, context, missingClassConsumer);
+ return internalDefinitionFor(type, context, foundClassConsumer, missingClassConsumer);
}
private DexClass internalDefinitionFor(
DexType type,
ProgramDerivedContext context,
+ BiConsumer<DexClass, ProgramDerivedContext> foundClassConsumer,
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
DexClass clazz = appInfo().definitionFor(type);
if (clazz == null) {
missingClassConsumer.accept(type, context);
return null;
}
- if (clazz.isNotProgramClass()) {
- addLiveNonProgramType(
- clazz.asClasspathOrLibraryClass(),
- (missingType, derivedContext) ->
- reportMissingClass(missingType, derivedContext.asProgramDerivedContext(context)));
- }
+ foundClassConsumer.accept(clazz, context);
return clazz;
}
@@ -772,7 +771,8 @@
private DexProgramClass getProgramClassOrNullFromReflectiveAccess(
DexType type, ProgramDefinition context) {
// To avoid that we report reflectively accessed types as missing.
- DexClass clazz = definitionFor(type, context, this::ignoreMissingClass);
+ DexClass clazz =
+ definitionFor(type, context, this::recordNonProgramClass, this::ignoreMissingClass);
return clazz != null && clazz.isProgramClass() ? clazz.asProgramClass() : null;
}
@@ -1805,8 +1805,16 @@
? this::reportMissingClass
: this::ignoreMissingClass;
for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
- recordTypeReference(innerClassAttribute.getInner(), clazz, missingClassConsumer);
- recordTypeReference(innerClassAttribute.getOuter(), clazz, missingClassConsumer);
+ recordTypeReference(
+ innerClassAttribute.getInner(),
+ clazz,
+ this::recordNonProgramClass,
+ missingClassConsumer);
+ recordTypeReference(
+ innerClassAttribute.getOuter(),
+ clazz,
+ this::recordNonProgramClass,
+ missingClassConsumer);
}
}
@@ -1828,10 +1836,12 @@
? this::reportMissingClass
: this::ignoreMissingClass;
if (enclosingMethod != null) {
- recordMethodReference(enclosingMethod, clazz, missingClassConsumer);
+ recordMethodReference(
+ enclosingMethod, clazz, this::recordNonProgramClass, missingClassConsumer);
} else {
DexType enclosingClass = enclosingMethodAttribute.getEnclosingClass();
- recordTypeReference(enclosingClass, clazz, missingClassConsumer);
+ recordTypeReference(
+ enclosingClass, clazz, this::recordNonProgramClass, missingClassConsumer);
}
}
@@ -2072,11 +2082,13 @@
private SingleResolutionResult resolveMethod(
DexMethod method, ProgramDefinition context, KeepReason reason) {
// Record the references in case they are not program types.
- recordMethodReference(method, context);
MethodResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
if (resolutionResult.isFailedResolution()) {
markFailedMethodResolutionTargets(
method, resolutionResult.asFailedResolution(), context, reason);
+ recordMethodReference(method, context, this::recordFoundClass, this::reportMissingClass);
+ } else {
+ recordMethodReference(method, context);
}
return resolutionResult.asSingleResolution();
}
@@ -2092,7 +2104,7 @@
assert resolutionResult.isFailedResolution();
markFailedMethodResolutionTargets(
method, resolutionResult.asFailedResolution(), context, reason);
- recordMethodReference(method, context);
+ recordMethodReference(method, context, this::recordFoundClass, this::reportMissingClass);
}
return resolutionResult.asSingleResolution();
}
@@ -2327,6 +2339,25 @@
});
}
+ private void recordFoundClass(DexClass clazz, ProgramDerivedContext context) {
+ if (clazz.isProgramClass()) {
+ if (context.isProgramContext()) {
+ markTypeAsLive(clazz, context.getContext().asProgramDefinition());
+ }
+ } else {
+ recordNonProgramClass(clazz, context);
+ }
+ }
+
+ private void recordNonProgramClass(DexClass clazz, ProgramDerivedContext context) {
+ if (!clazz.isProgramClass()) {
+ addLiveNonProgramType(
+ clazz.asClasspathOrLibraryClass(),
+ (missingType, derivedContext) ->
+ reportMissingClass(missingType, derivedContext.asProgramDerivedContext(context)));
+ }
+ }
+
private void ignoreMissingClass(DexType clazz) {
missingClassesBuilder.ignoreNewMissingClass(clazz);
}
@@ -2968,19 +2999,18 @@
return;
}
- // Note that all virtual methods derived from library methods are kept regardless of being
- // reachable, so the following only needs to consider reachable targets in the program.
- // TODO(b/70160030): Revise this to support tree shaking library methods on non-escaping types.
- DexProgramClass holder = getProgramClassOrNull(method.holder, context);
- if (holder == null) {
- // TODO(b/139464956): clean this.
- // Ensure that the full proto of the targeted method is referenced.
- recordMethodReference(method, context);
+ SingleResolutionResult resolution = resolveMethod(method, context, reason, interfaceInvoke);
+ if (resolution == null) {
return;
}
- SingleResolutionResult resolution = resolveMethod(method, context, reason, interfaceInvoke);
- if (resolution == null) {
+ // Note that all virtual methods derived from library methods are kept regardless of being
+ // reachable, so the following only needs to consider reachable targets in the program.
+ // TODO(b/70160030): Revise this to support tree shaking library methods on non-escaping types.
+ DexProgramClass initialResolutionHolder =
+ resolution.getInitialResolutionHolder().asProgramClass();
+ if (initialResolutionHolder == null) {
+ recordMethodReference(method, context);
return;
}
@@ -2996,7 +3026,8 @@
// If the method has already been marked, just report the new reason for the resolved target and
// save the context to ensure correct lookup of virtual dispatch targets.
ResolutionSearchKey resolutionSearchKey = new ResolutionSearchKey(method, interfaceInvoke);
- ProgramMethodSet seenContexts = getReachableVirtualTargets(holder).get(resolutionSearchKey);
+ ProgramMethodSet seenContexts =
+ getReachableVirtualTargets(initialResolutionHolder).get(resolutionSearchKey);
if (seenContexts != null) {
seenContexts.add(context);
graphReporter.registerMethod(resolution.getResolvedMethod(), reason);
@@ -3019,7 +3050,7 @@
// The method resolved and is accessible, so currently live overrides become live.
reachableVirtualTargets
- .computeIfAbsent(holder, ignoreArgument(HashMap::new))
+ .computeIfAbsent(initialResolutionHolder, ignoreArgument(HashMap::new))
.computeIfAbsent(resolutionSearchKey, ignoreArgument(ProgramMethodSet::create))
.add(context);
@@ -3544,7 +3575,7 @@
// Commit the pending synthetics and recompute subtypes.
appInfo = appInfo.rebuildWithClassHierarchy(app -> app);
appView.setAppInfo(appInfo);
- subtypingInfo = new SubtypingInfo(appView);
+ subtypingInfo = SubtypingInfo.create(appView);
// Finally once all synthesized items "exist" it is now safe to continue tracing. The new work
// items are enqueued and the fixed point will continue once this subroutine returns.
@@ -4066,7 +4097,7 @@
// Commit the pending synthetics and recompute subtypes.
appInfo = appInfo.rebuildWithClassHierarchy(app -> app);
appView.setAppInfo(appInfo);
- subtypingInfo = new SubtypingInfo(appView);
+ subtypingInfo = SubtypingInfo.create(appView);
syntheticAdditions.enqueueWorkItems(this);
@@ -4987,7 +5018,8 @@
}
public DexClass definitionFor(DexType type, ProgramDefinition context) {
- return enqueuer.definitionFor(type, context, enqueuer::ignoreMissingClass);
+ return enqueuer.definitionFor(
+ type, context, enqueuer::recordNonProgramClass, enqueuer::ignoreMissingClass);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index 98ffa2a..8596642 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -237,7 +237,6 @@
// Fields that are javac inlined are unsound as predicates for conditional rules.
// Ignore any such field members and record it for possible reporting later.
if (isFieldInlinedByJavaC(f)) {
- f.markAsInlinableByJavaC();
fieldsInlinedByJavaC.add(DexClassAndField.create(targetClass, f));
return false;
}
@@ -317,21 +316,9 @@
if (enqueuer.getMode().isFinalTreeShaking()) {
// Ignore any field value in the final tree shaking pass so it remains consistent with the
// initial pass.
- return field.isInlinableByJavaC();
+ return field.getIsInlinableByJavaC();
}
- if (!field.isStatic() || !field.isFinal()) {
- return false;
- }
- if (!field.hasExplicitStaticValue()) {
- return false;
- }
- if (field.getType().isPrimitiveType()) {
- return true;
- }
- if (field.getType() != appView.dexItemFactory().stringType) {
- return false;
- }
- return field.getStaticValue().isDexValueString();
+ return field.getOrComputeIsInlinableByJavaC(appView.dexItemFactory());
}
private void materializeIfRule(ProguardIfRule rule, Set<DexReference> preconditions) {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 9629fb2..2f7d3f4 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -62,6 +62,7 @@
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.OriginWithPosition;
import com.android.tools.r8.utils.PredicateSet;
+import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.TraversalContinuation;
@@ -1299,13 +1300,7 @@
throw new Unreachable();
}
} else if (context instanceof ProguardIdentifierNameStringRule) {
- if (item.isField()) {
- identifierNameStrings.add(item.asField().getReference());
- context.markAsUsed();
- } else if (item.isMethod()) {
- identifierNameStrings.add(item.asMethod().getReference());
- context.markAsUsed();
- }
+ evaluateIdentifierNameStringRule(item, context, ifRule);
} else if (context instanceof ConstantArgumentRule) {
if (item.isMethod()) {
keepParametersWithConstantValue.add(item.asMethod().getReference());
@@ -1559,6 +1554,36 @@
}
}
+ private void evaluateIdentifierNameStringRule(
+ Definition item, ProguardConfigurationRule context, ProguardIfRule ifRule) {
+ // Main dex rules should not contain -identifiernamestring rules.
+ assert !isMainDexRootSetBuilder();
+
+ // We don't expect -identifiernamestring rules to be used as the subsequent of -if rules.
+ assert ifRule == null;
+
+ if (item.isClass()) {
+ return;
+ }
+
+ if (item.isField()) {
+ DexClassAndField field = item.asField();
+ if (field.getDefinition().getOrComputeIsInlinableByJavaC(appView.dexItemFactory())) {
+ Reporter reporter = appView.reporter();
+ reporter.warning(
+ new StringDiagnostic(
+ "Rule matches the static final field `"
+ + field.toSourceString()
+ + "`, which may have been inlined: "
+ + context.toString(),
+ context.getOrigin()));
+ }
+ }
+
+ identifierNameStrings.add(item.asMember().getReference());
+ context.markAsUsed();
+ }
+
private boolean isInterfaceMethodNeedingDesugaring(ProgramDefinition item) {
return options.isInterfaceMethodDesugaringEnabled()
&& item.isMethod()
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index a7362e5..19f0adc 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -324,9 +324,8 @@
// The set of targets that must remain for proper resolution error cases should not be merged.
// TODO(b/192821424): Can be removed if handled.
- for (DexMethod method : appInfo.getFailedMethodResolutionTargets()) {
- markTypeAsPinned(method.holder, AbortReason.RESOLUTION_FOR_METHODS_MAY_CHANGE);
- }
+ extractPinnedItems(
+ appInfo.getFailedMethodResolutionTargets(), AbortReason.RESOLUTION_FOR_METHODS_MAY_CHANGE);
}
private <T extends DexReference> void extractPinnedItems(Iterable<T> items, AbortReason reason) {
@@ -1129,7 +1128,7 @@
return false;
}
- // Rewrite generic signatures before we merge fields.
+ // Rewrite generic signatures before we merge a base with a generic signature.
rewriteGenericSignatures(target, source, directMethods.values(), virtualMethods.values());
// Convert out of DefaultInstanceInitializerCode, since this piece of code will require lens
@@ -1333,16 +1332,18 @@
assert false : "Type should be present in generic signature";
return null;
}
+ Map<String, FieldTypeSignature> substitutionMap = new HashMap<>();
List<FormalTypeParameter> formals = source.getClassSignature().getFormalTypeParameters();
if (genericArgumentsToSuperType.size() != formals.size()) {
- // TODO(b/214509535): Correctly rewrite signature when type arguments is empty.
- assert genericArgumentsToSuperType.isEmpty() : "Invalid argument count to formals";
- return null;
- }
- Map<String, FieldTypeSignature> substitutionMap = new HashMap<>();
- for (int i = 0; i < formals.size(); i++) {
- // It is OK to override a generic type variable so we just use put.
- substitutionMap.put(formals.get(i).getName(), genericArgumentsToSuperType.get(i));
+ if (!genericArgumentsToSuperType.isEmpty()) {
+ assert false : "Invalid argument count to formals";
+ return null;
+ }
+ } else {
+ for (int i = 0; i < formals.size(); i++) {
+ // It is OK to override a generic type variable so we just use put.
+ substitutionMap.put(formals.get(i).getName(), genericArgumentsToSuperType.get(i));
+ }
}
return GenericSignaturePartialTypeArgumentApplier.build(
appView,
@@ -1512,6 +1513,7 @@
.setApiLevelForDefinition(method.getApiLevelForDefinition())
.setApiLevelForCode(method.getApiLevelForDefinition())
.setIsLibraryMethodOverride(method.isLibraryMethodOverride())
+ .setGenericSignature(method.getGenericSignature())
.build();
if (method.accessFlags.isPromotedToPublic()) {
// The bridge is now the public method serving the role of the original method, and should
@@ -1966,7 +1968,7 @@
}
@Override
- public DexField getRenamedFieldSignature(DexField originalField) {
+ public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
throw new Unreachable();
}
@@ -1981,7 +1983,7 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
throw new Unreachable();
}
@@ -2022,7 +2024,7 @@
@Override
public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
- DexMethod method) {
+ DexMethod method, GraphLens codeLens) {
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index 21af6a8..37965ec 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -86,6 +86,11 @@
}
@Override
+ public boolean isVerticalClassMergerLens() {
+ return true;
+ }
+
+ @Override
protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
Collection<DexType> originalTypes = mergedClasses.getSourcesFor(previous);
Iterable<DexType> currentType = IterableUtils.singleton(previous);
@@ -130,8 +135,8 @@
}
@Override
- protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
- return super.internalGetPreviousMethodSignature(
+ public DexMethod getPreviousMethodSignature(DexMethod method) {
+ return super.getPreviousMethodSignature(
originalMethodSignaturesForBridges.getOrDefault(method, method));
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
index 36c6573..574f518 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -109,8 +109,7 @@
}
DexType thisType = getHolder().getType();
DexType otherType = other.getHolder().getType();
- if (getKind().isFixedSuffixSynthetic) {
- // Fixed synthetics are non-shareable. Ordered by their unique type.
+ if (!getKind().isShareable()) {
return thisType.compareTo(otherType);
}
if (includeContext) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index a29bfcf..0cb0137 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -32,6 +31,7 @@
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -419,13 +419,15 @@
// Addition and creation of synthetic items.
- private DexProgramClass internalEnsureDexProgramClass(
+ private DexProgramClass internalEnsureFixedProgramClass(
SyntheticKind kind,
Consumer<SyntheticProgramClassBuilder> classConsumer,
Consumer<DexProgramClass> onCreationConsumer,
SynthesizingContext outerContext,
- DexType type,
AppView<?> appView) {
+ Function<SynthesizingContext, DexType> contextToType =
+ c -> SyntheticNaming.createFixedType(kind, c, appView.dexItemFactory());
+ DexType type = contextToType.apply(outerContext);
// Fast path is that the synthetic is already present. If so it must be a program class.
DexClass dexClass = appView.definitionFor(type);
if (dexClass != null) {
@@ -452,7 +454,8 @@
},
outerContext,
type,
- appView.dexItemFactory());
+ contextToType,
+ appView);
onCreationConsumer.accept(dexProgramClass);
return dexProgramClass;
}
@@ -463,15 +466,38 @@
Consumer<SyntheticProgramClassBuilder> fn,
SynthesizingContext outerContext,
DexType type,
- DexItemFactory factory) {
+ Function<SynthesizingContext, DexType> contextToType,
+ AppView<?> appView) {
+ registerSyntheticTypeRewriting(outerContext, contextToType, appView, type);
SyntheticProgramClassBuilder classBuilder =
- new SyntheticProgramClassBuilder(type, kind, outerContext, factory);
+ new SyntheticProgramClassBuilder(type, kind, outerContext, appView.dexItemFactory());
fn.accept(classBuilder);
DexProgramClass clazz = classBuilder.build();
addPendingDefinition(new SyntheticProgramClassDefinition(kind, outerContext, clazz));
return clazz;
}
+ private void registerSyntheticTypeRewriting(
+ SynthesizingContext outerContext,
+ Function<SynthesizingContext, DexType> contextToType,
+ AppView<?> appView,
+ DexType type) {
+ DexType rewrittenContextType =
+ appView.rewritePrefix.rewrittenContextType(
+ outerContext.getSynthesizingContextType(), appView);
+ if (rewrittenContextType == null) {
+ return;
+ }
+ SynthesizingContext synthesizingContext = SynthesizingContext.fromType(rewrittenContextType);
+ DexType rewrittenType = contextToType.apply(synthesizingContext);
+ appView.rewritePrefix.rewriteType(type, rewrittenType);
+ }
+
+ public DexProgramClass createClass(
+ SyntheticKind kind, UniqueContext context, AppView<?> appView) {
+ return createClass(kind, context, appView, ConsumerUtils.emptyConsumer());
+ }
+
public DexProgramClass createClass(
SyntheticKind kind,
UniqueContext context,
@@ -480,10 +506,12 @@
// Obtain the outer synthesizing context in the case the context itself is synthetic.
// This is to ensure a flat input-type -> synthetic-item mapping.
SynthesizingContext outerContext = getSynthesizingContext(context.getClassContext(), appView);
- DexType type =
- SyntheticNaming.createInternalType(
- kind, outerContext, context.getSyntheticSuffix(), appView.dexItemFactory());
- return internalCreateProgramClass(kind, fn, outerContext, type, appView.dexItemFactory());
+ Function<SynthesizingContext, DexType> contextToType =
+ c ->
+ SyntheticNaming.createInternalType(
+ kind, c, context.getSyntheticSuffix(), appView.dexItemFactory());
+ return internalCreateProgramClass(
+ kind, fn, outerContext, contextToType.apply(outerContext), contextToType, appView);
}
// TODO(b/172194101): Make this take a unique context.
@@ -493,8 +521,10 @@
AppView<?> appView,
Consumer<SyntheticProgramClassBuilder> fn) {
SynthesizingContext outerContext = internalGetOuterContext(context, appView);
- DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
- return internalCreateProgramClass(kind, fn, outerContext, type, appView.dexItemFactory());
+ Function<SynthesizingContext, DexType> contextToType =
+ c -> SyntheticNaming.createFixedType(kind, c, appView.dexItemFactory());
+ return internalCreateProgramClass(
+ kind, fn, outerContext, contextToType.apply(outerContext), contextToType, appView);
}
public DexProgramClass getExistingFixedClass(
@@ -530,8 +560,7 @@
Consumer<DexProgramClass> onCreationConsumer) {
assert kind.isFixedSuffixSynthetic;
SynthesizingContext outerContext = internalGetOuterContext(context, appView);
- DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
- return internalEnsureDexProgramClass(kind, fn, onCreationConsumer, outerContext, type, appView);
+ return internalEnsureFixedProgramClass(kind, fn, onCreationConsumer, outerContext, appView);
}
public ProgramMethod ensureFixedClassMethod(
@@ -589,13 +618,15 @@
+ " class.");
}
- private DexClasspathClass internalEnsureDexClasspathClass(
+ private DexClasspathClass internalEnsureFixedClasspathClass(
SyntheticKind kind,
Consumer<SyntheticClasspathClassBuilder> classConsumer,
Consumer<DexClasspathClass> onCreationConsumer,
SynthesizingContext outerContext,
- DexType type,
AppView<?> appView) {
+ Function<SynthesizingContext, DexType> contextToType =
+ (c) -> SyntheticNaming.createFixedType(kind, c, appView.dexItemFactory());
+ DexType type = contextToType.apply(outerContext);
synchronized (type) {
DexClass clazz = appView.definitionFor(type);
if (clazz != null) {
@@ -604,6 +635,7 @@
}
return clazz.asClasspathClass();
}
+ registerSyntheticTypeRewriting(outerContext, contextToType, appView, type);
SyntheticClasspathClassBuilder classBuilder =
new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory());
classConsumer.accept(classBuilder);
@@ -620,9 +652,8 @@
AppView<?> appView,
Consumer<SyntheticClasspathClassBuilder> classConsumer) {
SynthesizingContext outerContext = SynthesizingContext.fromType(contextType);
- DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
- return internalEnsureDexClasspathClass(
- kind, classConsumer, ignored -> {}, outerContext, type, appView);
+ return internalEnsureFixedClasspathClass(
+ kind, classConsumer, ignored -> {}, outerContext, appView);
}
public DexClasspathClass ensureFixedClasspathClass(
@@ -634,9 +665,8 @@
// Obtain the outer synthesizing context in the case the context itself is synthetic.
// This is to ensure a flat input-type -> synthetic-item mapping.
SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
- DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
- return internalEnsureDexClasspathClass(
- kind, classConsumer, onCreationConsumer, outerContext, type, appView);
+ return internalEnsureFixedClasspathClass(
+ kind, classConsumer, onCreationConsumer, outerContext, appView);
}
public DexClassAndMethod ensureFixedClasspathClassMethod(
@@ -703,8 +733,7 @@
// Obtain the outer synthesizing context in the case the context itself is synthetic.
// This is to ensure a flat input-type -> synthetic-item mapping.
SynthesizingContext outerContext = SynthesizingContext.fromType(contextType);
- DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
- return internalEnsureDexProgramClass(kind, fn, onCreationConsumer, outerContext, type, appView);
+ return internalEnsureFixedProgramClass(kind, fn, onCreationConsumer, outerContext, appView);
}
/** Create a single synthetic method item. */
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index c80a94b..62c7c6f 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -39,6 +39,7 @@
HORIZONTAL_INIT_TYPE_ARGUMENT_1(SYNTHETIC_CLASS_SEPARATOR + "IA$1", 6, false, true),
HORIZONTAL_INIT_TYPE_ARGUMENT_2(SYNTHETIC_CLASS_SEPARATOR + "IA$2", 7, false, true),
HORIZONTAL_INIT_TYPE_ARGUMENT_3(SYNTHETIC_CLASS_SEPARATOR + "IA$3", 8, false, true),
+ NON_FIXED_INIT_TYPE_ARGUMENT("$IA", 35, false),
// Method synthetics.
ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD("CheckNotZero", 27, true),
RECORD_HELPER("Record", 9, true),
@@ -100,6 +101,18 @@
return this == RECORD_TAG;
}
+ public boolean isShareable() {
+ if (isFixedSuffixSynthetic) {
+ // Fixed synthetics are non-shareable. Ordered by their unique type.
+ return false;
+ }
+ if (this == NON_FIXED_INIT_TYPE_ARGUMENT) {
+ // TODO(b/214901256): Sharing of synthetic classes may lead to duplicate method errors.
+ return false;
+ }
+ return true;
+ }
+
public static SyntheticKind fromDescriptor(String descriptor) {
for (SyntheticKind kind : values()) {
if (kind.descriptor.equals(descriptor)) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index c1dc2c9..d1264f6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -53,6 +53,8 @@
import com.android.tools.r8.horizontalclassmerging.Policy;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.MachineDesugarPrefixRewritingMapper;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.nest.Nest;
@@ -892,6 +894,15 @@
public LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
LegacyDesugaredLibrarySpecification.empty();
+ public PrefixRewritingMapper getPrefixRewritingMapper() {
+ if (testing.machineDesugaredLibrarySpecification != null) {
+ return new MachineDesugarPrefixRewritingMapper(
+ desugaredLibrarySpecification.getPrefixRewritingMapper(),
+ testing.machineDesugaredLibrarySpecification.getRewritingFlags());
+ }
+ return desugaredLibrarySpecification.getPrefixRewritingMapper();
+ }
+
public boolean relocatorCompilation = false;
// If null, no keep rules are recorded.
@@ -1264,6 +1275,8 @@
private boolean enabled = true;
private boolean enableMethodStaticizing = true;
+ private boolean forceSyntheticsForInstanceInitializers = false;
+
public void disableOptimization() {
enabled = false;
}
@@ -1279,6 +1292,10 @@
return enabled;
}
+ public boolean isForceSyntheticsForInstanceInitializersEnabled() {
+ return forceSyntheticsForInstanceInitializers;
+ }
+
public boolean isMethodStaticizingEnabled() {
return enableMethodStaticizing;
}
@@ -1292,6 +1309,12 @@
return this;
}
+ public CallSiteOptimizationOptions setForceSyntheticsForInstanceInitializers(
+ boolean forceSyntheticsForInstanceInitializers) {
+ this.forceSyntheticsForInstanceInitializers = forceSyntheticsForInstanceInitializers;
+ return this;
+ }
+
public CallSiteOptimizationOptions setEnableMethodStaticizing(boolean enableMethodStaticizing) {
this.enableMethodStaticizing = enableMethodStaticizing;
return this;
@@ -2315,4 +2338,12 @@
public boolean canParseNumbersWithPlusPrefix() {
return getMinApiLevel().isGreaterThan(AndroidApiLevel.K);
}
+
+ // Lollipop and Marshmallow devices do not correctly handle invoke-super when the static holder
+ // is higher up in the hierarchy than the method that the invoke-super should resolve to.
+ //
+ // See b/215573892.
+ public boolean canHaveSuperInvokeBug() {
+ return getMinApiLevel().isLessThan(AndroidApiLevel.N);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java
index 9c1c5af..b14dd86 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java
@@ -53,6 +53,11 @@
return backing.getOrDefault(wrap(member), defaultValue);
}
+ public V getOrDefault(K member, Supplier<V> defaultValue) {
+ V value = backing.get(wrap(member));
+ return value != null ? value : defaultValue.get();
+ }
+
public boolean isEmpty() {
return backing.isEmpty();
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelper.java b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelper.java
index 558fdf6..15efa1a 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelper.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelper.java
@@ -6,7 +6,9 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.FieldPut;
@@ -15,7 +17,6 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.Return;
-import com.android.tools.r8.ir.conversion.MethodProcessor;
/**
* Inserts check-cast instructions after vertical class merging when this is needed for the program
@@ -41,8 +42,13 @@
public static InterfaceTypeToClassTypeLensCodeRewriterHelper create(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
- MethodProcessor methodProcessor) {
- if (methodProcessor.isPrimaryMethodProcessor() && appView.hasVerticallyMergedClasses()) {
+ NonIdentityGraphLens graphLens,
+ GraphLens codeLens) {
+ NonIdentityGraphLens previousLens =
+ graphLens.find(lens -> lens.isVerticalClassMergerLens() || lens == codeLens);
+ if (previousLens != null
+ && previousLens != codeLens
+ && previousLens.isVerticalClassMergerLens()) {
return new InterfaceTypeToClassTypeLensCodeRewriterHelperImpl(appView, code);
}
return new EmptyInterfaceTypeToClassTypeLensCodeRewriterHelper();
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index 1c74df5..cf27d33 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.D8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -64,10 +65,13 @@
@Override
D8TestCompileResult internalCompile(
- Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
+ Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults)
throws CompilationFailedException {
libraryDesugaringTestConfiguration.configure(builder);
- ToolHelper.runD8(builder, optionsConsumer);
+ ToolHelper.runAndBenchmarkD8(builder, optionsConsumer, benchmarkResults);
return new D8TestCompileResult(
getState(),
app.get(),
diff --git a/src/test/java/com/android/tools/r8/DXTestBuilder.java b/src/test/java/com/android/tools/r8/DXTestBuilder.java
index 436f1e7..fcef696 100644
--- a/src/test/java/com/android/tools/r8/DXTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/DXTestBuilder.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.D8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -44,8 +45,12 @@
@Override
DXTestCompileResult internalCompile(
- Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
+ Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults)
throws CompilationFailedException {
+ assert benchmarkResults == null;
assert !libraryDesugaringTestConfiguration.isEnabled();
try {
Path dxOutputFolder = getState().getNewTempFolder();
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 6d34346..2808f05 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -110,8 +111,12 @@
@Override
ExternalR8TestCompileResult internalCompile(
- Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
+ Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults)
throws CompilationFailedException {
+ assert benchmarkResults == null;
assert !libraryDesugaringTestConfiguration.isEnabled();
try {
Path outputFolder = getState().getNewTempFolder();
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 8d5bdea..66e5fc8 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
@@ -64,8 +65,12 @@
@Override
ProguardTestCompileResult internalCompile(
- Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
+ Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults)
throws CompilationFailedException {
+ assert benchmarkResults == null;
assert !libraryDesugaringTestConfiguration.isEnabled();
try {
Path proguardOutputFolder = getState().getNewTempFolder();
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 090ebb5..b3a595a 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
@@ -71,7 +72,10 @@
@Override
R8TestCompileResult internalCompile(
- Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
+ Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults)
throws CompilationFailedException {
if (!keepRules.isEmpty()) {
builder.addProguardConfiguration(keepRules, Origin.unknown());
@@ -118,10 +122,11 @@
ToolHelper.addSyntheticProguardRulesConsumerForTesting(
builder, rules -> box.syntheticProguardRules = rules);
libraryDesugaringTestConfiguration.configure(builder);
- ToolHelper.runR8WithoutResult(
+ ToolHelper.runAndBenchmarkR8WithoutResult(
builder.build(),
optionsConsumer.andThen(
- options -> box.proguardConfiguration = options.getProguardConfiguration()));
+ options -> box.proguardConfiguration = options.getProguardConfiguration()),
+ benchmarkResults);
R8TestCompileResult compileResult =
new R8TestCompileResult(
getState(),
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index d72151f..eb7769e 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -806,7 +806,7 @@
computeAppViewWithClassHierarchy(app, keepConfig, optionsConsumer);
// Run the tree shaker to compute an instance of AppInfoWithLiveness.
ExecutorService executor = Executors.newSingleThreadExecutor();
- SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
+ SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
RootSet rootSet =
RootSet.builder(
appView, subtypingInfo, appView.options().getProguardConfiguration().getRules())
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 5b6996d..efe353d 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.debug.CfDebugTestConfig;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.debug.DexDebugTestConfig;
@@ -25,11 +26,13 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.TriFunction;
+import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
@@ -658,4 +661,19 @@
app.writeToZip(jarFile, OutputMode.DexIndexed);
return new Dex2OatTestRunResult(app, runtime, ToolHelper.runDex2OatRaw(jarFile, oatFile, vm));
}
+
+ public CR benchmarkCodeSize(BenchmarkResults results) throws IOException {
+ Path out = writeToZip();
+ Box<Long> size = new Box<>(0L);
+ ZipUtils.iter(
+ out,
+ (entry, stream) -> {
+ if ((getBackend().isDex() && entry.getName().endsWith(".dex"))
+ || getBackend().isCf() && entry.getName().endsWith(".class")) {
+ size.set(size.get() + entry.getSize());
+ }
+ });
+ results.addCodeSizeResult(size.get());
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index a66a4b2..be94525 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
@@ -110,7 +111,10 @@
}
abstract CR internalCompile(
- B builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
+ B builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults)
throws CompilationFailedException;
public T addArgumentPropagatorCodeScannerResultInspector(
@@ -183,7 +187,19 @@
dexItemFactory, verticallyMergedClasses))));
}
+ public CR benchmarkCompile(BenchmarkResults results) throws CompilationFailedException {
+ if (System.getProperty("com.android.tools.r8.printtimes") != null) {
+ allowStdoutMessages();
+ }
+ return internalCompileAndBenchmark(results);
+ }
+
public CR compile() throws CompilationFailedException {
+ return internalCompileAndBenchmark(null);
+ }
+
+ private CR internalCompileAndBenchmark(BenchmarkResults benchmark)
+ throws CompilationFailedException {
AndroidAppConsumers sink = new AndroidAppConsumers();
builder.setProgramConsumer(sink.wrapProgramConsumer(programConsumer));
if (mainDexClassesCollector != null || mainDexListConsumer != null) {
@@ -239,7 +255,7 @@
() -> new AssertionError("Unexpected print to stderr"))));
}
cr =
- internalCompile(builder, optionsConsumer, Suppliers.memoize(sink::build))
+ internalCompile(builder, optionsConsumer, Suppliers.memoize(sink::build), benchmark)
.addRunClasspathFiles(additionalRunClassPath);
if (isAndroidBuildVersionAdded) {
cr.setSystemProperty(AndroidBuildVersion.PROPERTY, "" + builder.getMinApiLevel());
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index bd5ad83..28625eb 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -120,6 +120,13 @@
return assertSuccessWithOutputThatMatches(is(expected));
}
+ public RR assertSuccessWithOutputIf(boolean condition, String expected) {
+ if (condition) {
+ return assertSuccessWithOutput(expected);
+ }
+ return self();
+ }
+
public RR assertSuccessWithEmptyOutput() {
return assertSuccessWithOutput("");
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 57fabd1..1534edb 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper.DexVm.Kind;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.desugar.desugaredlibrary.jdk11.DesugaredLibraryJDK11Undesugarer;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.Unreachable;
@@ -1252,9 +1253,25 @@
public static void runR8WithoutResult(
R8Command command, Consumer<InternalOptions> optionsConsumer)
throws CompilationFailedException {
+ runAndBenchmarkR8WithoutResult(command, optionsConsumer, null);
+ }
+
+ public static void runAndBenchmarkR8WithoutResult(
+ R8Command command,
+ Consumer<InternalOptions> optionsConsumer,
+ BenchmarkResults benchmarkResults)
+ throws CompilationFailedException {
InternalOptions internalOptions = command.getInternalOptions();
optionsConsumer.accept(internalOptions);
+ long start = 0;
+ if (benchmarkResults != null) {
+ start = System.nanoTime();
+ }
R8.runForTesting(command.getInputApp(), internalOptions);
+ if (benchmarkResults != null) {
+ long end = System.nanoTime();
+ benchmarkResults.addRuntimeRawResult(end - start);
+ }
}
public static AndroidApp runR8WithFullResult(
@@ -1324,6 +1341,14 @@
public static AndroidApp runD8(
D8Command.Builder builder, Consumer<InternalOptions> optionsConsumer)
throws CompilationFailedException {
+ return runAndBenchmarkD8(builder, optionsConsumer, null);
+ }
+
+ public static AndroidApp runAndBenchmarkD8(
+ D8Command.Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ BenchmarkResults benchmarkResults)
+ throws CompilationFailedException {
AndroidAppConsumers compatSink = new AndroidAppConsumers(builder);
D8Command command = builder.build();
InternalOptions options = command.getInternalOptions();
@@ -1331,7 +1356,15 @@
ExceptionUtils.withD8CompilationHandler(
options.reporter, () -> optionsConsumer.accept(options));
}
+ long start = 0;
+ if (benchmarkResults != null) {
+ start = System.nanoTime();
+ }
D8.runForTesting(command.getInputApp(), options);
+ if (benchmarkResults != null) {
+ long end = System.nanoTime();
+ benchmarkResults.addRuntimeRawResult(end - start);
+ }
return compatSink.build();
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java
new file mode 100644
index 0000000..3753d7f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public abstract class BenchmarkBase extends TestBase {
+
+ // Benchmarks must be configured with the "none" runtime as each config defines a singleton
+ // benchmark in golem.
+ public static List<Object[]> parametersFromConfigs(Iterable<BenchmarkConfig> configs) {
+ return buildParameters(configs, getTestParameters().withNoneRuntime().build());
+ }
+
+ private final BenchmarkConfig config;
+
+ protected BenchmarkBase(BenchmarkConfig config, TestParameters parameters) {
+ this.config = config;
+ parameters.assertNoneRuntime();
+ }
+
+ protected BenchmarkConfig getConfig() {
+ return config;
+ }
+
+ @Test
+ public void testBenchmarks() throws Exception {
+ config.run(temp);
+ }
+
+ public static BenchmarkRunner runner(BenchmarkConfig config) {
+ return BenchmarkRunner.runner(config);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
new file mode 100644
index 0000000..f75aabe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import com.android.tools.r8.benchmarks.helloworld.HelloWorldBenchmark;
+import com.android.tools.r8.errors.Unreachable;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BenchmarkCollection {
+
+ // Actual list of all configured benchmarks.
+ private final Map<BenchmarkIdentifier, BenchmarkConfig> benchmarks = new HashMap<>();
+
+ private void addBenchmark(BenchmarkConfig benchmark) {
+ BenchmarkIdentifier id = benchmark.getIdentifier();
+ if (benchmarks.containsKey(id)) {
+ throw new Unreachable("Duplicate definition of benchmark with name and target: " + id);
+ }
+ benchmarks.put(id, benchmark);
+ }
+
+ public BenchmarkConfig getBenchmark(BenchmarkIdentifier benchmark) {
+ return benchmarks.get(benchmark);
+ }
+
+ public static BenchmarkCollection computeCollection() {
+ BenchmarkCollection collection = new BenchmarkCollection();
+ // Every benchmark that should be active on golem must be setup in this method.
+ HelloWorldBenchmark.configs().forEach(collection::addBenchmark);
+ return collection;
+ }
+
+ /** Compute and print the golem configuration. */
+ public static void main(String[] args) throws IOException {
+ new BenchmarkCollectionPrinter(System.out)
+ .printGolemConfig(computeCollection().benchmarks.values());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
new file mode 100644
index 0000000..e3ede15
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
@@ -0,0 +1,213 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import static com.android.tools.r8.utils.ListUtils.map;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.google.common.base.Strings;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class BenchmarkCollectionPrinter {
+
+ private interface RunnableIO {
+ void run() throws IOException;
+ }
+
+ private static final PrintStream QUIET =
+ new PrintStream(
+ new OutputStream() {
+ @Override
+ public void write(int b) {
+ // ignore
+ }
+ });
+
+ // Internal state for printing the benchmark config for golem.
+ private int currentIndent = 0;
+ private final PrintStream out;
+
+ public BenchmarkCollectionPrinter(PrintStream out) {
+ this.out = out;
+ }
+
+ private void scopeBraces(RunnableIO fn) throws IOException {
+ print("{");
+ indentScope(2, fn);
+ print("}");
+ }
+
+ private void indentScope(RunnableIO fn) throws IOException {
+ indentScope(2, fn);
+ }
+
+ private void indentScope(int spaces, RunnableIO fn) throws IOException {
+ currentIndent += spaces;
+ fn.run();
+ currentIndent -= spaces;
+ }
+
+ private static String quote(String str) {
+ return "\"" + str + "\"";
+ }
+
+ private void print(String string) {
+ printIndented(string, currentIndent);
+ }
+
+ private void printSemi(String string) {
+ print(string + ";");
+ }
+
+ private void printIndented(String string, int indent) {
+ out.print(Strings.repeat(" ", indent));
+ out.println(string);
+ }
+
+ public void printGolemConfig(Collection<BenchmarkConfig> benchmarks) throws IOException {
+ Path jdkHome = getJdkHome();
+ Map<String, List<BenchmarkConfig>> nameToTargets = new HashMap<>();
+ benchmarks.forEach(
+ b -> nameToTargets.computeIfAbsent(b.getName(), k -> new ArrayList<>()).add(b));
+ List<String> sortedNames = new ArrayList<>(nameToTargets.keySet());
+ sortedNames.sort(String::compareTo);
+ print(
+ "// AUTOGENERATED FILE from"
+ + " src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java");
+ print("");
+ printSemi("part of r8_config");
+ print("");
+ print("createTestBenchmarks() {");
+ indentScope(
+ () -> {
+ print("final cpus = [\"Lenovo M90\"];");
+ addGolemResource("openjdk", Paths.get(jdkHome + ".tar.gz"));
+ for (String benchmarkName : sortedNames) {
+ List<BenchmarkConfig> benchmarkTargets = nameToTargets.get(benchmarkName);
+ assert !benchmarkTargets.isEmpty();
+ scopeBraces(() -> printBenchmarkBlock(benchmarkName, benchmarkTargets));
+ }
+ });
+ print("}");
+ }
+
+ private void printBenchmarkBlock(String benchmarkName, List<BenchmarkConfig> benchmarkTargets)
+ throws IOException {
+ printSemi("final name = " + quote(benchmarkName));
+ printSemi("final group = new GroupBenchmark(name + \"Group\", [])");
+ // NOTE: It appears these must be consistent for each target now?
+ boolean hasWarmup = false;
+ String suite = null;
+ List<String> metrics = null;
+ for (BenchmarkConfig benchmark : benchmarkTargets) {
+ if (metrics == null) {
+ // TODO: Verify equal on other runs.
+ hasWarmup = benchmark.hasTimeWarmupRuns();
+ suite = benchmark.getSuite().getDartName();
+ metrics = map(benchmark.getMetrics(), BenchmarkMetric::getDartType);
+ }
+ scopeBraces(
+ () -> {
+ printSemi("final target = " + quote(benchmark.getTarget().getGolemName()));
+ printSemi("final options = group.addTargets(noImplementation, [target])");
+ printSemi("options.cpus = cpus");
+ printSemi("options.isScript = true");
+ printSemi("options.fromRevision = " + benchmark.getFromRevision());
+ print("options.mainFile =");
+ indentScope(
+ 4,
+ () ->
+ printSemi(
+ quote(
+ "tools/run_benchmark.py --golem"
+ + " --target "
+ + benchmark.getTarget().getIdentifierName()
+ + " --benchmark "
+ + benchmark.getName())));
+ printSemi("options.resources.add(openjdk)");
+ });
+ }
+
+ List<String> finalMetrics = metrics;
+ String finalSuite = suite;
+ boolean finalHasWarmup = hasWarmup;
+ scopeBraces(
+ () -> {
+ printSemi("final metrics = " + StringUtils.join(", ", finalMetrics, BraceType.SQUARE));
+ printSemi("group.addBenchmark(name, metrics)");
+ printSemi(finalSuite + ".addBenchmark(name)");
+ if (finalHasWarmup) {
+ printSemi("final warmupName = name + \"Warmup\"");
+ printSemi("group.addBenchmark(warmupName, [Metric.RunTimeRaw])");
+ printSemi(finalSuite + ".addBenchmark(warmupName)");
+ }
+ });
+ }
+
+ private void addGolemResource(String name, Path tarball) throws IOException {
+ Path shaFile = Paths.get(tarball.toString() + ".sha1");
+ downloadDependency(shaFile);
+ String sha256 = computeSha256(tarball);
+ String shaFileContent = getShaFileContent(shaFile);
+ print("final " + name + " = BenchmarkResource(" + quote("") + ",");
+ indentScope(
+ 4,
+ () -> {
+ print("type: BenchmarkResourceType.Storage,");
+ print("uri: " + quote("gs://r8-deps/" + shaFileContent) + ",");
+ // Make dart formatter happy.
+ if (currentIndent > 6) {
+ print("hash:");
+ indentScope(4, () -> print(quote(sha256) + ","));
+ } else {
+ print("hash: " + quote(sha256) + ",");
+ }
+ print("extract: " + quote("gz") + ");");
+ });
+ }
+
+ private static Path getJdkHome() throws IOException {
+ ProcessBuilder builder = new ProcessBuilder("python", "tools/jdk.py");
+ ProcessResult result = ToolHelper.runProcess(builder, QUIET);
+ if (result.exitCode != 0) {
+ throw new Unreachable("Unexpected failure to determine jdk home: " + result);
+ }
+ return Paths.get(result.stdout.trim());
+ }
+
+ private static String computeSha256(Path path) throws IOException {
+ Hasher hasher = Hashing.sha256().newHasher();
+ return hasher.putBytes(Files.readAllBytes(path)).hash().toString();
+ }
+
+ private static String getShaFileContent(Path path) throws IOException {
+ return String.join("\n", Files.readAllLines(path)).trim();
+ }
+
+ private static void downloadDependency(Path path) throws IOException {
+ ProcessBuilder builder =
+ new ProcessBuilder(
+ "download_from_google_storage", "-n", "-b", "r8-deps", "-u", "-s", path.toString());
+ ProcessResult result = ToolHelper.runProcess(builder, QUIET);
+ if (result.exitCode != 0) {
+ throw new Unreachable("Unable to download dependency '" + path + "'\n" + result);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
new file mode 100644
index 0000000..09d7a6e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
@@ -0,0 +1,168 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.rules.TemporaryFolder;
+
+public class BenchmarkConfig {
+
+ public static class Builder {
+
+ private String name = null;
+ private BenchmarkMethod method = null;
+ private BenchmarkTarget target = null;
+ private Set<BenchmarkMetric> metrics = new HashSet<>();
+ private BenchmarkSuite suite = BenchmarkSuite.getDefault();
+ private int fromRevision = -1;
+
+ private boolean timeWarmupRuns = false;
+
+ private Builder() {}
+
+ public BenchmarkConfig build() {
+ if (name == null) {
+ throw new Unreachable("Benchmark name must be set");
+ }
+ if (method == null) {
+ throw new Unreachable("Benchmark method must be set");
+ }
+ if (target == null) {
+ throw new Unreachable("Benchmark target must be set");
+ }
+ if (metrics.isEmpty()) {
+ throw new Unreachable("Benchmark must have at least one metric to measure");
+ }
+ if (suite == null) {
+ throw new Unreachable("Benchmark must have a suite");
+ }
+ if (fromRevision < 0) {
+ throw new Unreachable("Benchmark must specify from which golem revision it is valid");
+ }
+ if (timeWarmupRuns && !metrics.contains(BenchmarkMetric.RunTimeRaw)) {
+ throw new Unreachable("Benchmark with warmup time must measure RunTimeRaw");
+ }
+ return new BenchmarkConfig(
+ name, method, target, ImmutableSet.copyOf(metrics), suite, fromRevision, timeWarmupRuns);
+ }
+
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder setTarget(BenchmarkTarget target) {
+ this.target = target;
+ return this;
+ }
+
+ public Builder setMethod(BenchmarkMethod method) {
+ this.method = method;
+ return this;
+ }
+
+ public Builder measureRunTimeRaw() {
+ metrics.add(BenchmarkMetric.RunTimeRaw);
+ return this;
+ }
+
+ public Builder measureCodeSize() {
+ metrics.add(BenchmarkMetric.CodeSize);
+ return this;
+ }
+
+ public Builder setSuite(BenchmarkSuite suite) {
+ this.suite = suite;
+ return this;
+ }
+
+ public Builder setFromRevision(int fromRevision) {
+ this.fromRevision = fromRevision;
+ return this;
+ }
+
+ public Builder timeWarmupRuns() {
+ this.timeWarmupRuns = true;
+ return this;
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private final BenchmarkIdentifier id;
+ private final BenchmarkMethod method;
+ private final ImmutableSet<BenchmarkMetric> metrics;
+ private final BenchmarkSuite suite;
+ private final int fromRevision;
+ private final boolean timeWarmupRuns;
+
+ private BenchmarkConfig(
+ String name,
+ BenchmarkMethod benchmarkMethod,
+ BenchmarkTarget target,
+ ImmutableSet<BenchmarkMetric> metrics,
+ BenchmarkSuite suite,
+ int fromRevision,
+ boolean timeWarmupRuns) {
+ this.id = new BenchmarkIdentifier(name, target);
+ this.method = benchmarkMethod;
+ this.metrics = metrics;
+ this.suite = suite;
+ this.fromRevision = fromRevision;
+ this.timeWarmupRuns = timeWarmupRuns;
+ }
+
+ public BenchmarkIdentifier getIdentifier() {
+ return id;
+ }
+
+ public String getName() {
+ return id.getName();
+ }
+
+ public String getWarmupName() {
+ if (!timeWarmupRuns) {
+ throw new Unreachable("Invalid attempt at getting warmup benchmark name");
+ }
+ return getName() + "Warmup";
+ }
+
+ public BenchmarkTarget getTarget() {
+ return id.getTarget();
+ }
+
+ public Set<BenchmarkMetric> getMetrics() {
+ return metrics;
+ }
+
+ public boolean hasMetric(BenchmarkMetric metric) {
+ return metrics.contains(metric);
+ }
+
+ public BenchmarkSuite getSuite() {
+ return suite;
+ }
+
+ public int getFromRevision() {
+ return fromRevision;
+ }
+
+ public boolean hasTimeWarmupRuns() {
+ return timeWarmupRuns;
+ }
+
+ public void run(TemporaryFolder temp) throws Exception {
+ method.run(this, temp);
+ }
+
+ @Override
+ public String toString() {
+ return id.getName() + "/" + id.getTarget().getIdentifierName();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkIdentifier.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkIdentifier.java
new file mode 100644
index 0000000..f9ac65e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkIdentifier.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import com.android.tools.r8.utils.structural.Equatable;
+import com.android.tools.r8.utils.structural.Ordered;
+import java.util.Comparator;
+import java.util.Objects;
+
+public class BenchmarkIdentifier implements Ordered<BenchmarkIdentifier> {
+
+ private final String name;
+ private final BenchmarkTarget target;
+
+ public static BenchmarkIdentifier parse(String benchmarkName, String targetIdentifier) {
+ for (BenchmarkTarget target : BenchmarkTarget.values()) {
+ if (target.getIdentifierName().equals(targetIdentifier)) {
+ return new BenchmarkIdentifier(benchmarkName, target);
+ }
+ }
+ return null;
+ }
+
+ public BenchmarkIdentifier(String name, BenchmarkTarget target) {
+ this.name = name;
+ this.target = target;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public BenchmarkTarget getTarget() {
+ return target;
+ }
+
+ @Override
+ public int compareTo(BenchmarkIdentifier other) {
+ return Comparator.comparing(BenchmarkIdentifier::getName)
+ .thenComparing(BenchmarkIdentifier::getTarget)
+ .compare(this, other);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return Equatable.equalsImpl(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, target);
+ }
+
+ @Override
+ public String toString() {
+ return "BenchmarkIdentifier{" + "name='" + name + '\'' + ", target=" + target + '}';
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java
new file mode 100644
index 0000000..3e5a86a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import org.junit.rules.TemporaryFolder;
+
+public class BenchmarkMainEntryRunner {
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 2) {
+ throw new RuntimeException("Invalid arguments. Expected exactly one benchmark and target");
+ }
+ String benchmarkName = args[0];
+ String targetIdentifier = args[1];
+ BenchmarkIdentifier identifier = BenchmarkIdentifier.parse(benchmarkName, targetIdentifier);
+ if (identifier == null) {
+ throw new RuntimeException("Invalid identifier identifier: " + benchmarkName);
+ }
+ BenchmarkCollection collection = BenchmarkCollection.computeCollection();
+ BenchmarkConfig config = collection.getBenchmark(identifier);
+ if (config == null) {
+ throw new RuntimeException("Unknown identifier: " + identifier);
+ }
+ TemporaryFolder temp = new TemporaryFolder();
+ temp.create();
+ config.run(temp);
+ temp.delete();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMethod.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMethod.java
new file mode 100644
index 0000000..8c7e372
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMethod.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import org.junit.rules.TemporaryFolder;
+
+@FunctionalInterface
+public interface BenchmarkMethod {
+
+ void run(BenchmarkConfig config, TemporaryFolder temp) throws Exception;
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java
new file mode 100644
index 0000000..9483499
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMetric.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+public enum BenchmarkMetric {
+ RunTimeRaw,
+ CodeSize;
+
+ public String getDartType() {
+ return "Metric." + name();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
new file mode 100644
index 0000000..cf55b86
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+import com.android.tools.r8.benchmarks.BenchmarkRunner.ResultMode;
+import com.android.tools.r8.errors.Unreachable;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import it.unimi.dsi.fastutil.longs.LongList;
+
+public class BenchmarkResults {
+
+ private final boolean isWarmupResults;
+ private final LongList runtimeRawResults = new LongArrayList();
+ private final LongList codeSizeResults = new LongArrayList();
+
+ public static BenchmarkResults create() {
+ return new BenchmarkResults(false);
+ }
+
+ public static BenchmarkResults createForWarmup() {
+ return new BenchmarkResults(true);
+ }
+
+ private BenchmarkResults(boolean isWarmupResults) {
+ this.isWarmupResults = isWarmupResults;
+ }
+
+ private String getName(BenchmarkConfig config) {
+ return isWarmupResults ? config.getWarmupName() : config.getName();
+ }
+
+ public void addRuntimeRawResult(long result) {
+ runtimeRawResults.add(result);
+ }
+
+ public void addCodeSizeResult(long result) {
+ codeSizeResults.add(result);
+ }
+
+ private static void verifyMetric(BenchmarkMetric metric, boolean expected, boolean actual) {
+ if (expected != actual) {
+ throw new Unreachable(
+ "Mismatched config and result for "
+ + metric.name()
+ + ". Expected by config: "
+ + expected
+ + ", but has result: "
+ + actual);
+ }
+ }
+
+ private void verifyConfigAndResults(BenchmarkConfig config) {
+ verifyMetric(
+ BenchmarkMetric.RunTimeRaw,
+ config.getMetrics().contains(BenchmarkMetric.RunTimeRaw),
+ !runtimeRawResults.isEmpty());
+ verifyMetric(
+ BenchmarkMetric.CodeSize,
+ config.getMetrics().contains(BenchmarkMetric.CodeSize),
+ !codeSizeResults.isEmpty());
+ }
+
+ public static String prettyTime(long nanoTime) {
+ return "" + (nanoTime / 1000000) + "ms";
+ }
+
+ private void printRunTimeRaw(BenchmarkConfig config, long duration) {
+ System.out.println(getName(config) + "(RunTimeRaw): " + prettyTime(duration));
+ }
+
+ private void printCodeSize(BenchmarkConfig config, long bytes) {
+ System.out.println(getName(config) + "(CodeSize): " + bytes);
+ }
+
+ public void printResults(ResultMode mode, BenchmarkConfig config) {
+ verifyConfigAndResults(config);
+ if (config.hasMetric(BenchmarkMetric.RunTimeRaw)) {
+ long sum = runtimeRawResults.stream().mapToLong(l -> l).sum();
+ if (mode == ResultMode.SUM) {
+ printRunTimeRaw(config, sum);
+ } else if (mode == ResultMode.AVERAGE) {
+ printRunTimeRaw(config, sum / runtimeRawResults.size());
+ }
+ }
+ if (!isWarmupResults && config.hasMetric(BenchmarkMetric.CodeSize)) {
+ long size = codeSizeResults.getLong(0);
+ for (int i = 1; i < codeSizeResults.size(); i++) {
+ if (size != codeSizeResults.getLong(i)) {
+ throw new Unreachable(
+ "Unexpected code size difference: " + size + " and " + codeSizeResults.getLong(i));
+ }
+ }
+ printCodeSize(config, size);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java
new file mode 100644
index 0000000..f0cb0b8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+public class BenchmarkRunner {
+
+ public interface BenchmarkRunnerFunction {
+ void run(BenchmarkResults results) throws Exception;
+ }
+
+ public enum ResultMode {
+ AVERAGE,
+ SUM;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+ }
+
+ private final BenchmarkConfig config;
+ private int warmups = 0;
+ private int iterations = 1;
+ private ResultMode resultMode = ResultMode.AVERAGE;
+
+ private BenchmarkRunner(BenchmarkConfig config) {
+ this.config = config;
+ }
+
+ public static BenchmarkRunner runner(BenchmarkConfig config) {
+ return new BenchmarkRunner(config);
+ }
+
+ public BenchmarkRunner setWarmupIterations(int iterations) {
+ this.warmups = iterations;
+ return this;
+ }
+
+ public BenchmarkRunner setBenchmarkIterations(int iterations) {
+ this.iterations = iterations;
+ return this;
+ }
+
+ public BenchmarkRunner reportResultAverage() {
+ resultMode = ResultMode.AVERAGE;
+ return this;
+ }
+
+ public BenchmarkRunner reportResultSum() {
+ resultMode = ResultMode.SUM;
+ return this;
+ }
+
+ public void run(BenchmarkRunnerFunction fn) throws Exception {
+ long warmupTotalTime = 0;
+ BenchmarkResults warmupResults = BenchmarkResults.createForWarmup();
+ if (warmups > 0) {
+ long start = System.nanoTime();
+ for (int i = 0; i < warmups; i++) {
+ fn.run(warmupResults);
+ }
+ warmupTotalTime = System.nanoTime() - start;
+ }
+ BenchmarkResults results = BenchmarkResults.create();
+ long start = System.nanoTime();
+ for (int i = 0; i < iterations; i++) {
+ fn.run(results);
+ }
+ long benchmarkTotalTime = System.nanoTime() - start;
+ System.out.println(
+ "Benchmark results for "
+ + config.getName()
+ + " on target "
+ + config.getTarget().getIdentifierName());
+ if (warmups > 0) {
+ printMetaInfo("warmup", warmups, warmupTotalTime);
+ if (config.hasTimeWarmupRuns()) {
+ warmupResults.printResults(resultMode, config);
+ }
+ }
+ printMetaInfo("benchmark", iterations, benchmarkTotalTime);
+ results.printResults(resultMode, config);
+ System.out.println();
+ }
+
+ private void printMetaInfo(String kind, int iterations, long totalTime) {
+ System.out.println(" " + kind + " reporting mode: " + resultMode);
+ System.out.println(" " + kind + " iterations: " + iterations);
+ System.out.println(" " + kind + " total time: " + BenchmarkResults.prettyTime(totalTime));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkSuite.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkSuite.java
new file mode 100644
index 0000000..1068f72
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkSuite.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+/** Enumeration of the benchmark suites on Golem. */
+public enum BenchmarkSuite {
+ R8_BENCHMARKS("R8Benchmarks", "suite"),
+ D8_BENCHMARKS("D8KeyBenchmarks", "d8KeySuite"),
+ D8_INCREMENTAL_BENCHMARKS("D8IncrementalBenchmarks", "incrementalSuite"),
+ OPENSOURCE_BENCHMARKS("OpenSourceAppDumps", "dumpsSuite"),
+ MEMORY_BENCHMARKS("R8MemoryBenchmarks", "r8MemorySuite"),
+ RETRACE_BENCHMARKS("R8RetraceBenchmarks", "r8RetraceSuite");
+
+ private final String golemName;
+ private final String dartName;
+
+ public static BenchmarkSuite getDefault() {
+ return R8_BENCHMARKS;
+ }
+
+ BenchmarkSuite(String golemName, String dartName) {
+ this.golemName = golemName;
+ this.dartName = dartName;
+ }
+
+ /** The name as shown in golems listings. */
+ public String getGolemName() {
+ return golemName;
+ }
+
+ /** The variable name used for the suite in the benchmarks.dart script. */
+ public String getDartName() {
+ return dartName;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTarget.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTarget.java
new file mode 100644
index 0000000..cda7f6e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTarget.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2022, 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.benchmarks;
+
+public enum BenchmarkTarget {
+ // Possible dashboard targets on golem.
+ D8("d8", "D8"),
+ R8_COMPAT("r8-compat", "R8"),
+ R8_NON_COMPAT("r8", "R8-full"),
+ R8_FORCE_OPT("r8-force", "R8-full-minify-optimize-shrink");
+
+ private final String idName;
+ private final String golemName;
+
+ BenchmarkTarget(String idName, String golemName) {
+ this.idName = idName;
+ this.golemName = golemName;
+ }
+
+ public String getGolemName() {
+ return golemName;
+ }
+
+ public String getIdentifierName() {
+ return idName;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java b/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java
new file mode 100644
index 0000000..b91e5b1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java
@@ -0,0 +1,136 @@
+// Copyright (c) 2022, 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.benchmarks.helloworld;
+
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.benchmarks.BenchmarkBase;
+import com.android.tools.r8.benchmarks.BenchmarkConfig;
+import com.android.tools.r8.benchmarks.BenchmarkMethod;
+import com.android.tools.r8.benchmarks.BenchmarkTarget;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.util.List;
+import java.util.function.Function;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Example of setting up a benchmark based on the testing infrastructure. */
+@RunWith(Parameterized.class)
+public class HelloWorldBenchmark extends BenchmarkBase {
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return parametersFromConfigs(configs());
+ }
+
+ public HelloWorldBenchmark(BenchmarkConfig config, TestParameters parameters) {
+ super(config, parameters);
+ }
+
+ /** Static method to add benchmarks to the benchmark collection. */
+ public static List<BenchmarkConfig> configs() {
+ Builder<BenchmarkConfig> benchmarks = ImmutableList.builder();
+ makeBenchmark(BenchmarkTarget.D8, HelloWorldBenchmark::benchmarkD8, benchmarks);
+ makeBenchmark(BenchmarkTarget.R8_NON_COMPAT, HelloWorldBenchmark::benchmarkR8, benchmarks);
+ return benchmarks.build();
+ }
+
+ // Options/parameter setup to define variants of the benchmark above.
+ private static class Options {
+ final BenchmarkTarget target;
+ final Backend backend;
+ final boolean includeLibrary;
+ final AndroidApiLevel minApi = AndroidApiLevel.B;
+
+ public Options(BenchmarkTarget target, Backend backend, boolean includeLibrary) {
+ this.target = target;
+ this.backend = backend;
+ this.includeLibrary = includeLibrary;
+ }
+
+ public String getName() {
+ // The name include each non-target option for the variants to ensure unique benchmarks.
+ String backendString = backend.isCf() ? "Cf" : "Dex";
+ String libraryString = includeLibrary ? "" : "NoLib";
+ return "HelloWorld" + backendString + libraryString;
+ }
+ }
+
+ private static void makeBenchmark(
+ BenchmarkTarget target,
+ Function<Options, BenchmarkMethod> method,
+ ImmutableList.Builder<BenchmarkConfig> benchmarks) {
+ for (boolean includeLibrary : BooleanUtils.values()) {
+ for (Backend backend : Backend.values()) {
+ Options options = new Options(target, backend, includeLibrary);
+ benchmarks.add(
+ BenchmarkConfig.builder()
+ // The benchmark is required to have a unique combination of name and target.
+ .setName(options.getName())
+ .setTarget(target)
+ // The benchmark is required to have at least one metric.
+ .measureRunTimeRaw()
+ .measureCodeSize()
+ // The benchmark is required to have a runner method which defines the actual
+ // execution.
+ .setMethod(method.apply(options))
+ // The benchmark is required to set a "golem from revision".
+ // Find this value by looking at the current revision on golem.
+ .setFromRevision(11900)
+ // The benchmark can optionally time the warmup. This is not needed to use a warmup
+ // in the actual run, only to include it as its own benchmark entry on golem.
+ .timeWarmupRuns()
+ .build());
+ }
+ }
+ }
+
+ public static BenchmarkMethod benchmarkD8(Options options) {
+ return (config, temp) ->
+ runner(config)
+ .setWarmupIterations(1)
+ .setBenchmarkIterations(100)
+ .reportResultSum()
+ .run(
+ results ->
+ testForD8(temp, options.backend)
+ .setMinApi(options.minApi)
+ .applyIf(!options.includeLibrary, TestBuilder::addLibraryFiles)
+ .addProgramClasses(TestClass.class)
+ // Compile and emit RunTimeRaw measure.
+ .benchmarkCompile(results)
+ // Measure the output size.
+ .benchmarkCodeSize(results));
+ }
+
+ public static BenchmarkMethod benchmarkR8(Options options) {
+ return (config, temp) ->
+ runner(config)
+ .setWarmupIterations(1)
+ .setBenchmarkIterations(4)
+ .reportResultSum()
+ .run(
+ results ->
+ testForR8(temp, options.backend)
+ .applyIf(!options.includeLibrary, b -> b.addLibraryFiles().addDontWarn("*"))
+ .setMinApi(options.minApi)
+ .addProgramClasses(TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ // Compile and emit RunTimeRaw measure.
+ .benchmarkCompile(results)
+ // Measure the output size.
+ .benchmarkCodeSize(results));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodWithSuperMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodWithSuperMethodMergingTest.java
new file mode 100644
index 0000000..0f0fb29
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodWithSuperMethodMergingTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2022, 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AbstractMethodWithSuperMethodMergingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertIsCompleteMergeGroup(B1.class, B2.class))
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccess();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ boolean unknownButAlwaysTrue = System.currentTimeMillis() > 0;
+ B1 c1 = unknownButAlwaysTrue ? new C1() : new C2();
+ C3 c3 = unknownButAlwaysTrue ? new C3() : null;
+ System.out.println(c1.foo());
+ System.out.println(c3.foo());
+ }
+ }
+
+ abstract static class A {
+
+ public int foo() {
+ return 0;
+ }
+ }
+
+ @NoVerticalClassMerging
+ abstract static class B1 extends A {
+
+ @Override
+ public abstract int foo();
+ }
+
+ @NoVerticalClassMerging
+ abstract static class B2 extends A {}
+
+ @NoHorizontalClassMerging
+ static class C1 extends B1 {
+
+ @Override
+ @NeverInline
+ public int foo() {
+ return System.identityHashCode(this);
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class C2 extends B1 {
+
+ @Override
+ @NeverInline
+ public int foo() {
+ return System.identityHashCode(this);
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class C3 extends B2 {}
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
index 055ab8d..93bfcef 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
@@ -37,30 +37,25 @@
.assertSuccessWithOutputLines("42", "13", "7", "print a", "print b")
.inspect(
codeInspector -> {
- ClassSubject aClassSubject = codeInspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
- FieldSubject classIdFieldSubject =
- aClassSubject.uniqueFieldWithName(ClassMerger.CLASS_ID_FIELD_NAME);
- assertThat(classIdFieldSubject, isPresent());
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ FieldSubject classIdFieldSubject =
+ aClassSubject.uniqueFieldWithName(ClassMerger.CLASS_ID_FIELD_NAME);
+ assertThat(classIdFieldSubject, isPresent());
- MethodSubject firstInitSubject = aClassSubject.init("int");
- assertThat(firstInitSubject, isPresent());
- assertThat(
- firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
+ MethodSubject firstInitSubject = aClassSubject.init("int");
+ assertThat(firstInitSubject, isPresent());
+ assertThat(firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- ClassSubject synthesizedClass = getSynthesizedArgumentClassSubject(codeInspector);
+ MethodSubject otherInitSubject = aClassSubject.init("int", "int");
+ assertThat(otherInitSubject, isPresent());
+ assertThat(otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject otherInitSubject =
- aClassSubject.init("int", synthesizedClass.getFinalName());
- assertThat(otherInitSubject, isPresent());
- assertThat(
- otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
+ MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
+ assertThat(printSubject, isPresent());
+ assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
- assertThat(printSubject, isPresent());
- assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
-
- assertThat(codeInspector.clazz(B.class), not(isPresent()));
+ assertThat(codeInspector.clazz(B.class), not(isPresent()));
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
index 0dd1f3e..61432d2 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
@@ -37,30 +37,25 @@
.assertSuccessWithOutputLines("7", "42", "13", "print a", "print b")
.inspect(
codeInspector -> {
- ClassSubject aClassSubject = codeInspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
- FieldSubject classIdFieldSubject =
- aClassSubject.uniqueFieldWithName(ClassMerger.CLASS_ID_FIELD_NAME);
- assertThat(classIdFieldSubject, isPresent());
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ FieldSubject classIdFieldSubject =
+ aClassSubject.uniqueFieldWithName(ClassMerger.CLASS_ID_FIELD_NAME);
+ assertThat(classIdFieldSubject, isPresent());
- MethodSubject firstInitSubject = aClassSubject.init("int");
- assertThat(firstInitSubject, isPresent());
- assertThat(
- firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
+ MethodSubject firstInitSubject = aClassSubject.init("int");
+ assertThat(firstInitSubject, isPresent());
+ assertThat(firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- ClassSubject synthesizedClass = getSynthesizedArgumentClassSubject(codeInspector);
+ MethodSubject otherInitSubject = aClassSubject.init("int", "int");
+ assertThat(otherInitSubject, isPresent());
+ assertThat(otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject otherInitSubject =
- aClassSubject.init("int", synthesizedClass.getFinalName());
- assertThat(otherInitSubject, isPresent());
- assertThat(
- otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
+ MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
+ assertThat(printSubject, isPresent());
+ assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
- assertThat(printSubject, isPresent());
- assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
-
- assertThat(codeInspector.clazz(B.class), not(isPresent()));
+ assertThat(codeInspector.clazz(B.class), not(isPresent()));
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
index 6094157..d02f16a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import org.junit.Test;
@@ -53,25 +52,10 @@
assertThat(aClassSubject, isPresent());
assertEquals(
2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
-
- ClassSubject nullArgumentClassSubject =
- inspector.allClasses().stream()
- .filter(
- clazz ->
- SyntheticItemsTestUtils.isHorizontalInitializerTypeArgument(
- clazz.getOriginalReference()))
- .findFirst()
- .orElseThrow(RuntimeException::new);
-
assertThat(
aClassSubject.method("void", "<init>", "java.lang.Object", "int"), isPresent());
assertThat(
- aClassSubject.method(
- "void",
- "<init>",
- "java.lang.Object",
- "int",
- nullArgumentClassSubject.getFinalName()),
+ aClassSubject.method("void", "<init>", "java.lang.Object", "int", "int"),
isPresent());
})
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java
new file mode 100644
index 0000000..fc00578
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2022, 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class HorizontalClassMergingOfInitArgumentTypesTest extends TestBase {
+
+ @Parameter(0)
+ public boolean enableHorizontalClassMerging;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, horizontal class merging: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> {
+ options.callSiteOptimizationOptions().setForceSyntheticsForInstanceInitializers(true);
+ options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging);
+ })
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ int expectedNumberOfSynthetics =
+ 2 - BooleanUtils.intValue(enableHorizontalClassMerging);
+ assertEquals(3 + expectedNumberOfSynthetics, inspector.allClasses().size());
+ assertThat(inspector.clazz(Main.class), isPresent());
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(B.class), isPresent());
+ assertEquals(
+ expectedNumberOfSynthetics,
+ inspector.allClasses().stream()
+ .filter(
+ clazz ->
+ SyntheticItemsTestUtils.isExternalNonFixedInitializerTypeArgument(
+ clazz.getOriginalReference()))
+ .count());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A()", "A(Object)", "B()", "B(Object)");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ String arg = args.length > 0 ? args[1] : null;
+ System.out.println(new A());
+ System.out.println(new A(arg));
+ System.out.println(new B());
+ System.out.println(new B(arg));
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class A {
+
+ boolean unused;
+
+ A() {}
+
+ // Unused argument removal will rewrite this into A(A$$ExternalSynthetic$IA0)
+ A(Object unused) {
+ this.unused = true;
+ }
+
+ @Override
+ public String toString() {
+ return unused ? "A(Object)" : "A()";
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class B {
+
+ boolean unused;
+
+ B() {}
+
+ // Unused argument removal will rewrite this into B(B$$ExternalSynthetic$IA0)
+ B(Object unused) {
+ this.unused = true;
+ }
+
+ @Override
+ public String toString() {
+ return unused ? "B(Object)" : "B()";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
index 9b45774..660dae3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
@@ -26,6 +26,7 @@
.addInnerClasses(getClass())
.addProgramClasses(A.class, B.class)
.addKeepMainRule(Main.class)
+ .enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
index a88c32b..049e3cb 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsNot.not;
+import com.android.tools.r8.KeepConstantArguments;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
@@ -24,6 +25,13 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .assertMergedInto(C.class, A.class)
+ .assertMergedInto(D.class, B.class)
+ .assertNoOtherClassesMerged())
+ .enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -34,14 +42,14 @@
codeInspector -> {
assertThat(codeInspector.clazz(A.class), isPresent());
assertThat(codeInspector.clazz(B.class), isPresent());
- // C has been merged into A.
- assertThat(codeInspector.clazz(C.class), not(isPresent()));
- assertThat(codeInspector.clazz(A.class).init("long"), isPresent());
+ // C has been merged into A.
+ assertThat(codeInspector.clazz(C.class), not(isPresent()));
+ assertThat(codeInspector.clazz(A.class).init("long"), isPresent());
- // D has been merged into B.
- assertThat(codeInspector.clazz(D.class), not(isPresent()));
- ClassSubject bClassSubject = codeInspector.clazz(B.class);
- assertThat(bClassSubject.init("boolean"), isPresent());
+ // D has been merged into B.
+ assertThat(codeInspector.clazz(D.class), not(isPresent()));
+ ClassSubject bClassSubject = codeInspector.clazz(B.class);
+ assertThat(bClassSubject.init("boolean"), isPresent());
});
}
@@ -69,6 +77,7 @@
@NeverClassInline
public static class C {
+ @KeepConstantArguments
public C(long v) {
System.out.println(v);
}
@@ -76,6 +85,7 @@
@NeverClassInline
public static class D {
+ @KeepConstantArguments
public D(boolean v) {
System.out.println(v);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/B.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/B.java
index 6157508..8bd1e0b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/B.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/B.java
@@ -4,11 +4,13 @@
package com.android.tools.r8.classmerging.horizontal.testclasses;
+import com.android.tools.r8.KeepConstantArguments;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
@NeverClassInline
public class B {
+ @KeepConstantArguments
protected B(String a) {
System.out.println(a);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 3f95a85..b056931 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -1076,9 +1076,6 @@
"classmerging.SimpleInterfaceAccessTest$OtherSimpleInterfaceImpl",
"classmerging.pkg.SimpleInterfaceImplRetriever",
"classmerging.pkg.SimpleInterfaceImplRetriever$SimpleInterfaceImpl");
- if (parameters.isCfRuntime()) {
- preservedClassNames.add("classmerging.SimpleInterfaceAccessTest$1");
- }
runTest(
testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java
new file mode 100644
index 0000000..27b7093
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2022, 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.classmerging.vertical;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.KeepConstantArguments;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
+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.MethodSubject;
+import java.lang.reflect.TypeVariable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class VerticalClassMergingWithMissingTypeArgsSubstitutionTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test()
+ public void test() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(A.class)
+ .addKeepAttributeSignature()
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(B.class))
+ .enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
+ .enableConstantArgumentAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("T", "Hello World")
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject = inspector.clazz(C.class);
+ assertThat(classSubject, isPresentAndRenamed());
+ assertEquals(
+ "<T:Ljava/lang/Object;>L" + binaryName(A.class) + "<Ljava/lang/Object;>;",
+ classSubject.getFinalSignatureAttribute());
+ MethodSubject bar = classSubject.uniqueMethodWithName("bar");
+ assertThat(bar, isPresentAndRenamed());
+ assertEquals("(TT;)V", bar.getFinalSignatureAttribute());
+ // The NeverInline is transferred to the private vertically merged method, making
+ // it hard to lookup.
+ MethodSubject movedFooSubject =
+ classSubject.uniqueMethodThatMatches(
+ method ->
+ method.getMethod().getReference() != bar.getMethod().getReference()
+ && !method.isInstanceInitializer());
+ assertThat(movedFooSubject, isPresentAndRenamed());
+ assertEquals(
+ "(Ljava/lang/Object;)Ljava/lang/Object;",
+ movedFooSubject.getFinalSignatureAttribute());
+ });
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ C<String> stringC = new C<>();
+ for (TypeVariable<? extends Class<? extends C>> typeParameter :
+ stringC.getClass().getTypeParameters()) {
+ System.out.println(typeParameter.getName());
+ }
+ stringC.bar("Hello World");
+ }
+ }
+
+ static class A<T> {}
+
+ static class B<T> extends A<T> {
+
+ @KeepConstantArguments
+ @NoMethodStaticizing
+ @NeverInline
+ public T foo(T t) {
+ if (System.currentTimeMillis() == 0) {
+ throw new RuntimeException("Foo");
+ }
+ return t;
+ }
+ }
+
+ static class C<T> extends B {
+
+ @NoMethodStaticizing
+ @KeepConstantArguments
+ @NeverInline
+ void bar(T t) {
+ System.out.println(foo(t));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index b3eb86b..d1e9746 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -10,11 +10,17 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -34,14 +40,19 @@
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
+ private final boolean machineSpec;
- @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ @Parameters(name = "machine: {0}, {2}, shrink: {1}")
public static List<Object[]> data() {
return buildParameters(
- BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ getTestParameters().withDexRuntimes().withAllApiLevels().build());
}
- public CustomCollectionTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ public CustomCollectionTest(
+ boolean machineSpec, boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.machineSpec = machineSpec;
this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
@@ -49,6 +60,22 @@
private final String EXECUTOR =
"com.android.tools.r8.desugar.desugaredlibrary.CustomCollectionTest$Executor";
+ private void setMachineSpec(InternalOptions opt) {
+ if (!machineSpec) {
+ return;
+ }
+ try {
+ HumanDesugaredLibrarySpecification human =
+ new LegacyToHumanSpecificationConverter()
+ .convert(opt.desugaredLibrarySpecification, getLibraryFile(), opt);
+ MachineDesugaredLibrarySpecification machine =
+ new HumanToMachineSpecificationConverter().convert(human, getLibraryFile(), opt);
+ opt.testing.machineDesugaredLibrarySpecification = machine;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@Test
public void testCustomCollectionD8() throws Exception {
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
@@ -56,6 +83,7 @@
testForD8()
.addLibraryFiles(getLibraryFile())
.addInnerClasses(CustomCollectionTest.class)
+ .addOptionsModification(this::setMachineSpec)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
@@ -78,6 +106,7 @@
Path jar =
testForD8(Backend.CF)
.addInnerClasses(CustomCollectionTest.class)
+ .addOptionsModification(this::setMachineSpec)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
@@ -133,6 +162,7 @@
testForR8(Backend.DEX)
.addLibraryFiles(getLibraryFile())
.addInnerClasses(CustomCollectionTest.class)
+ .addOptionsModification(this::setMachineSpec)
.setMinApi(parameters.getApiLevel())
.addKeepClassAndMembersRules(Executor.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index 606f253..2bd50b2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -153,6 +153,15 @@
"org/openjdk/tests/java/util/stream/FindAnyOpTest.java"
};
+ private boolean streamCloseTestShouldSucceed() {
+ if (!isJDK11DesugaredLibrary()) {
+ return false;
+ }
+ // TODO(b/216047740): Investigate if this runs on Dalvik VMs.
+ // StreamCloseTest relies on suppressed exceptions which may not work on Dalvik VMs.
+ return parameters.getDexRuntimeVersion().isNewerThan(Version.V4_4_4);
+ }
+
private Map<String, String> getSuccessfulTests() {
Map<String, String> runnableTests = getRunnableTests(SUCCESSFUL_RUNNABLE_TESTS);
if (isJDK11DesugaredLibrary()) {
@@ -160,9 +169,9 @@
if (parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0)) {
runnableTests.putAll(getRunnableTests(SUCCESSFUL_RUNNABLE_TESTS_ON_JDK11_AND_V7));
}
- if (!parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.K)) {
- runnableTests.putAll(getRunnableTests(STREAM_CLOSE_TESTS));
- }
+ }
+ if (streamCloseTestShouldSucceed()) {
+ runnableTests.putAll(getRunnableTests(STREAM_CLOSE_TESTS));
}
return runnableTests;
}
@@ -172,14 +181,11 @@
if (!isJDK11DesugaredLibrary()) {
runnableTests.putAll(getRunnableTests(SUCCESSFUL_RUNNABLE_TESTS_ON_JDK11_ONLY));
runnableTests.putAll(getRunnableTests(SUCCESSFUL_RUNNABLE_TESTS_ON_JDK11_AND_V7));
+ } else if (!parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0)) {
+ runnableTests.putAll(getRunnableTests(SUCCESSFUL_RUNNABLE_TESTS_ON_JDK11_AND_V7));
+ }
+ if (!streamCloseTestShouldSucceed()) {
runnableTests.putAll(getRunnableTests(STREAM_CLOSE_TESTS));
- } else {
- if (parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.K)) {
- runnableTests.putAll(getRunnableTests(STREAM_CLOSE_TESTS));
- }
- if (!parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0)) {
- runnableTests.putAll(getRunnableTests(SUCCESSFUL_RUNNABLE_TESTS_ON_JDK11_AND_V7));
- }
}
return runnableTests;
}
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java b/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
index 436dc43..5fe49e5 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
@@ -71,8 +71,8 @@
String restStackTrace = fullStackTrace.substring(topStackTraceEnd);
// Check that top stack trace always has the version marker.
assertThat(topStackTrace, containsString("fakeStackEntry"));
- // Check that top stack has the D8 entry (from tests the non-renamed entry is ToolHelper.runD8).
- assertThat(topStackTrace, containsString("com.android.tools.r8.ToolHelper.runD8("));
+ // Check that top stack has the D8 entry (from tests the non-renamed entry is ToolHelper.runX).
+ assertThat(topStackTrace, containsString("com.android.tools.r8.ToolHelper.run"));
// Check that the stack trace always has the suppressed info.
assertThat(restStackTrace, containsString(StringUtils.LINE_SEPARATOR + "\tSuppressed:"));
// Custom test checks.
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
index 97d8b7a..de2364e 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
@@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.internal;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.core.AnyOf.anyOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -122,6 +124,11 @@
.assertAllWarningMessagesMatch(
anyOf(
containsString("Expected stack map table for method with non-linear control flow."),
- containsString("Ignoring option: -outjars")));
+ containsString("Ignoring option: -outjars"),
+ allOf(
+ startsWith(
+ "Rule matches the static final field "
+ + "`java.lang.String com.google.protobuf."),
+ containsString("which may have been inlined: -identifiernamestring"))));
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
index 8538449..2f98c5c 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -61,7 +61,7 @@
.toDirect();
appView = AppView.createForR8(program);
appView.setAppServices(AppServices.builder(appView).build());
- subtypingInfo = new SubtypingInfo(appView);
+ subtypingInfo = SubtypingInfo.create(appView);
}
private AppInfoWithClassHierarchy appInfo() {
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
index c0f2478..dbde5fd 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
@@ -56,7 +55,7 @@
.compile()
.assertAllInfoMessagesMatch(
containsString("Proguard configuration rule does not match anything"))
- .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .apply(this::inspectWarningMessages)
.inspect(this::inspect)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines(
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index 3f9e228..9ea0db9 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -82,8 +81,7 @@
.compile()
.assertAllInfoMessagesMatch(
containsString("Proguard configuration rule does not match anything"))
- .assertAllWarningMessagesMatch(
- equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .apply(this::inspectWarningMessages)
.inspect(this::inspect);
for (String main : mains) {
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index fcc6f54..63dae9c 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -6,9 +6,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
-import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -97,8 +95,7 @@
.compile()
.assertAllInfoMessagesMatch(
containsString("Proguard configuration rule does not match anything"))
- .assertAllWarningMessagesMatch(
- equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .apply(this::inspectWarningMessages)
.inspect(
outputInspector -> {
verifyMapAndRequiredFieldsAreKept(inputInspector, outputInspector);
@@ -374,10 +371,7 @@
.compile()
.assertAllInfoMessagesMatch(
containsString("Proguard configuration rule does not match anything"))
- .assertAllWarningMessagesMatch(
- anyOf(
- equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
- containsString("required for default or static interface methods desugaring")))
+ .apply(this::inspectWarningMessages)
.inspect(
inspector ->
assertRewrittenProtoSchemasMatch(new CodeInspector(PROGRAM_FILES), inspector));
@@ -405,10 +399,7 @@
.compile()
.assertAllInfoMessagesMatch(
containsString("Proguard configuration rule does not match anything"))
- .assertAllWarningMessagesMatch(
- anyOf(
- equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
- containsString("required for default or static interface methods desugaring")))
+ .apply(this::inspectWarningMessages)
.inspect(
outputInspector -> {
verifyUnusedExtensionsAreRemoved(inputInspector, outputInspector);
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java b/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
index c0ce178..23d7007 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
@@ -5,10 +5,16 @@
package com.android.tools.r8.internal.proto;
import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getInfoValueFromMessageInfoConstructionInvoke;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexItemFactory;
@@ -136,4 +142,16 @@
}
return result;
}
+
+ void inspectWarningMessages(R8TestCompileResult compileResult) {
+ compileResult.assertAllWarningMessagesMatch(
+ anyOf(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
+ allOf(
+ startsWith(
+ "Rule matches the static final field `java.lang.String com.google."
+ + "protobuf.proto2_registryGeneratedExtensionRegistryLite."
+ + "CONTAINING_TYPE_"),
+ containsString("`, which may have been inlined: -identifiernamestring"))));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index 6f5d826..be71b20 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -74,7 +74,7 @@
AppView<AppInfoWithClassHierarchy> appView = AppView.createForR8(application.asDirect());
appView.setAppServices(AppServices.builder(appView).build());
ExecutorService executorService = ThreadUtils.getExecutorService(options);
- SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
+ SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
appView.setRootSet(
RootSet.builder(
appView,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerNullableStaticGetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerNullableStaticGetTest.java
new file mode 100644
index 0000000..0a6e376
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerNullableStaticGetTest.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2022, 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.classinliner;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ClassInlinerNullableStaticGetTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class);
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Singleton.get().greet();
+ Singleton.set();
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(new Singleton());
+ }
+ }
+ }
+
+ @NoVerticalClassMerging
+ static class Singleton {
+
+ static Singleton INSTANCE;
+
+ static Singleton get() {
+ return INSTANCE;
+ }
+
+ static void set() {
+ INSTANCE = new SingletonImpl();
+ }
+
+ void greet() {
+ System.out.println("Hello!");
+ }
+ }
+
+ static class SingletonImpl extends Singleton {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index 5987d3c..15f68fc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -183,8 +183,7 @@
Lists.newArrayList(
"STATIC: String SimpleWithSideEffects.bar(String)",
"STATIC: String SimpleWithSideEffects.foo()",
- "STATIC: String TrivialTestClass.next()",
- "SimpleWithSideEffects SimpleWithSideEffects.INSTANCE"),
+ "STATIC: String TrivialTestClass.next()"),
references(clazz, "testSimpleWithSideEffects", "void"));
ClassSubject simpleWithSideEffects = inspector.clazz(SimpleWithSideEffects.class);
@@ -315,8 +314,7 @@
Lists.newArrayList(
"STATIC: String movetohost.CandidateOkSideEffects.bar(String)",
"STATIC: String movetohost.CandidateOkSideEffects.foo()",
- "STATIC: String movetohost.MoveToHostTestClass.next()",
- "movetohost.CandidateOkSideEffects movetohost.HostOkSideEffects.INSTANCE"),
+ "STATIC: String movetohost.MoveToHostTestClass.next()"),
references(clazz, "testOkSideEffects", "void"));
assertThat(inspector.clazz(HostOkSideEffects.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/ConstantArgumentUpdateGenericSignatureTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/ConstantArgumentUpdateGenericSignatureTest.java
new file mode 100644
index 0000000..2619ab8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/ConstantArgumentUpdateGenericSignatureTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2022, 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.unusedarguments;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
+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.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConstantArgumentUpdateGenericSignatureTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributeSignature()
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World")
+ .inspect(
+ inspector -> {
+ ClassSubject classA = inspector.clazz(A.class);
+ assertThat(classA, isPresentAndRenamed());
+ MethodSubject foo =
+ classA.uniqueMethodThatMatches(method -> !method.isInstanceInitializer());
+ assertThat(foo, isPresent());
+ assertNull(foo.getFinalSignatureAttribute());
+ assertEquals("void a()", foo.getFinalSignature().toString());
+ });
+ }
+
+ @NeverClassInline
+ public static class A<T> {
+
+ @NeverInline
+ @NoMethodStaticizing
+ public void foo(T t) {
+ System.out.println(t);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A<String>().foo("Hello World");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/ConstantArgumentWithUnusedArgumentUpdateGenericSignatureTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/ConstantArgumentWithUnusedArgumentUpdateGenericSignatureTest.java
new file mode 100644
index 0000000..ea624dc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/ConstantArgumentWithUnusedArgumentUpdateGenericSignatureTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2022, 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.unusedarguments;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
+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.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConstantArgumentWithUnusedArgumentUpdateGenericSignatureTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributeSignature()
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World", "0")
+ .inspect(
+ inspector -> {
+ ClassSubject classA = inspector.clazz(A.class);
+ assertThat(classA, isPresentAndRenamed());
+ MethodSubject foo =
+ classA.uniqueMethodThatMatches(method -> !method.isInstanceInitializer());
+ assertThat(foo, isPresent());
+ assertNull(foo.getFinalSignatureAttribute());
+ assertEquals("void a(int)", foo.getFinalSignature().toString());
+ });
+ }
+
+ @NeverClassInline
+ public static class A<T> {
+
+ @NeverInline
+ @NoMethodStaticizing
+ public void foo(T t, int count, T unused) {
+ System.out.println(t);
+ System.out.println(count);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A<String>().foo("Hello World", args.length, args.length > 0 ? args[0] : "Hello World");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/StaticizeUpdateGenericSignatureTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/StaticizeUpdateGenericSignatureTest.java
new file mode 100644
index 0000000..da960e7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/StaticizeUpdateGenericSignatureTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2022, 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.unusedarguments;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.KeepConstantArguments;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+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.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StaticizeUpdateGenericSignatureTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributeSignature()
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
+ .enableConstantArgumentAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World")
+ .inspect(
+ inspector -> {
+ ClassSubject classA = inspector.clazz(A.class);
+ assertThat(classA, isPresentAndRenamed());
+ MethodSubject foo = classA.uniqueMethod();
+ assertThat(foo, isPresent());
+ assertNull(foo.getFinalSignatureAttribute());
+ assertEquals(
+ "java.lang.String a(java.lang.Object)", foo.getFinalSignature().toString());
+ });
+ }
+
+ @NeverClassInline
+ public static class A<T> {
+
+ @NeverInline
+ @NeverPropagateValue
+ @KeepConstantArguments
+ public String foo(T t) {
+ return "Hello " + t;
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A<String>().foo("World"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentUpdateGenericSignatureTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentUpdateGenericSignatureTest.java
new file mode 100644
index 0000000..4ebb10e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentUpdateGenericSignatureTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2022, 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.unusedarguments;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.NoMethodStaticizing;
+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.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnusedArgumentUpdateGenericSignatureTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributeSignature()
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
+ .enableNoMethodStaticizingAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World")
+ .inspect(
+ inspector -> {
+ ClassSubject classA = inspector.clazz(A.class);
+ assertThat(classA, isPresentAndRenamed());
+ MethodSubject foo =
+ classA.uniqueMethodThatMatches(method -> !method.isInstanceInitializer());
+ assertThat(foo, isPresent());
+ assertNull(foo.getFinalSignatureAttribute());
+ assertEquals("java.lang.String a()", foo.getFinalSignature().toString());
+ });
+ }
+
+ @NeverClassInline
+ public static class A<T> {
+
+ @NeverInline
+ @NeverPropagateValue
+ @NoMethodStaticizing
+ public String foo(T t) {
+ return "Hello World";
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A<String>().foo(args.length > 0 ? args[0] : "Hello World!"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index b05928d..24f1672 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
@@ -34,6 +35,7 @@
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
+import java.util.function.Consumer;
import org.junit.Test;
public class RegisterMoveSchedulerTest {
@@ -84,8 +86,11 @@
}
@Override
- public boolean replaceCurrentInstructionByInitClassIfPossible(
- AppView<AppInfoWithLiveness> appView, IRCode code, DexType type) {
+ public boolean removeOrReplaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ DexType type,
+ Consumer<InitClass> consumer) {
throw new Unimplemented();
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRawTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRawTest.java
new file mode 100644
index 0000000..7686ac3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRawTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
+import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromJavaType;
+
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.kotlin.metadata.type_raw_lib.JavaLibraryClass;
+import com.android.tools.r8.kotlin.metadata.type_raw_lib.JavaLibraryClass.GenericClass;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteRawTest extends KotlinMetadataTestBase {
+
+ private final String EXPECTED = StringUtils.lines("Hello World");
+ private static final String PKG_LIB = PKG + ".type_raw_lib";
+ private static final String PKG_APP = PKG + ".type_raw_app";
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ getKotlinTestParameters()
+ .withCompilersStartingFromIncluding(MIN_SUPPORTED_VERSION)
+ .withAllTargetVersions()
+ .build());
+ }
+
+ public MetadataRewriteRawTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
+ this.parameters = parameters;
+ }
+
+ private static Path javaLibZip;
+
+ @BeforeClass
+ public static void before() throws Exception {
+ Path zipFile = getStaticTemp().newFolder().toPath().resolve("out.jar");
+ javaLibZip =
+ ZipBuilder.builder(zipFile)
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFileForTestClass(JavaLibraryClass.class),
+ ToolHelper.getClassFileForTestClass(GenericClass.class))
+ .build();
+ }
+
+ private static final KotlinCompileMemoizer libJars =
+ getCompileMemoizer(getKotlinFileInTest(getBinaryNameFromJavaType(PKG_LIB), "lib"))
+ .configure(kotlinCompilerTool -> kotlinCompilerTool.addClasspathFiles(javaLibZip));
+ private final TestParameters parameters;
+
+ @Test
+ public void smokeTest() throws Exception {
+ Path libJar = libJars.getForConfiguration(kotlinc, targetVersion);
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar, javaLibZip)
+ .addSourceFiles(getKotlinFileInTest(getBinaryNameFromJavaType(PKG_APP), "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar, javaLibZip)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testMetadataForLib() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(
+ kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar(), javaLibZip)
+ .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addKeepRules("-keep class " + PKG_LIB + ".ClassWithRawType { *; }")
+ .addKeepAttributes(
+ ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
+ .compile()
+ .inspect(
+ inspector ->
+ assertEqualMetadata(
+ new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion)),
+ inspector,
+ (addedStrings, addedNonInitStrings) -> {}))
+ .writeToZip();
+ Path main =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar, javaLibZip)
+ .addSourceFiles(getKotlinFileInTest(getBinaryNameFromJavaType(PKG_APP), "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(
+ kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar, javaLibZip)
+ .addClasspath(main)
+ .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/type_raw_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/type_raw_app/main.kt
new file mode 100644
index 0000000..7cb36c2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/type_raw_app/main.kt
@@ -0,0 +1,11 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.type_raw_app
+
+import com.android.tools.r8.kotlin.metadata.type_raw_lib.ClassWithRawType
+
+fun main() {
+ println(ClassWithRawType().returnsRaw())
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/type_raw_lib/JavaLibraryClass.java b/src/test/java/com/android/tools/r8/kotlin/metadata/type_raw_lib/JavaLibraryClass.java
new file mode 100644
index 0000000..a46422e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/type_raw_lib/JavaLibraryClass.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.type_raw_lib;
+
+public abstract class JavaLibraryClass {
+
+ public static GenericClass getGeneric() {
+ return new GenericClass();
+ }
+
+ public static class GenericClass<T> {
+
+ @Override
+ public String toString() {
+ return "Hello World";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/type_raw_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/type_raw_lib/lib.kt
new file mode 100644
index 0000000..df9a05a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/type_raw_lib/lib.kt
@@ -0,0 +1,10 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.type_raw_lib
+
+class ClassWithRawType {
+
+ fun returnsRaw() = JavaLibraryClass.getGeneric();
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingSuperTest.java b/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingSuperTest.java
new file mode 100644
index 0000000..1fade81
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingSuperTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.memberrebinding;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.utils.AndroidApiLevel.B;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a reproduction of b/215573892.
+@RunWith(Parameterized.class)
+public class LibraryMemberRebindingSuperTest extends TestBase {
+
+ private final String EXPECTED =
+ StringUtils.lines("ProgramClass::foo", "LibrarySub::foo", "LibraryBase::foo");
+ private final String R8_INVALID = StringUtils.lines("ProgramClass::foo", "LibraryBase::foo");
+
+ private final Class<?>[] LIBRARY_CLASSES =
+ new Class<?>[] {LibraryBase.class, LibrarySub.class, LibrarySubSub.class};
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addLibraryClasses(LIBRARY_CLASSES)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(ProgramClass.class, Main.class)
+ .applyIf(
+ parameters.isDexRuntime(),
+ builder -> builder.addRunClasspathFiles(buildOnDexRuntime(parameters, LIBRARY_CLASSES)))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryClasses(LIBRARY_CLASSES)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(ProgramClass.class, Main.class)
+ .apply(setMockApiLevelForMethod(LibraryBase.class.getDeclaredMethod("foo"), B))
+ .apply(setMockApiLevelForMethod(LibrarySub.class.getDeclaredMethod("foo"), B))
+ .apply(setMockApiLevelForClass(LibraryBase.class, B))
+ .apply(setMockApiLevelForClass(LibrarySub.class, B))
+ .apply(setMockApiLevelForClass(LibrarySubSub.class, B))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
+ .addKeepAllClassesRule()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathClasses(LIBRARY_CLASSES)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ public static class LibraryBase {
+
+ public void foo() {
+ System.out.println("LibraryBase::foo");
+ }
+ }
+
+ public static class LibrarySub extends LibraryBase {
+
+ @Override
+ public void foo() {
+ System.out.println("LibrarySub::foo");
+ super.foo();
+ }
+ }
+
+ public static class LibrarySubSub extends LibrarySub {}
+
+ public static class ProgramClass extends LibrarySubSub {
+
+ @Override
+ public void foo() {
+ System.out.println("ProgramClass::foo");
+ super.foo();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new ProgramClass().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java
index 24c31c2..a86f95a 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java
@@ -72,7 +72,12 @@
assertThat(
getSystemService,
CodeMatchers.invokesMethodWithHolderAndName(
- typeName(LibrarySub.class), "getSystemService"));
+ typeName(
+ parameters.isCfRuntime()
+ || parameters.getApiLevel().isLessThan(AndroidApiLevel.N)
+ ? LibrarySubSub.class
+ : LibrarySub.class),
+ "getSystemService"));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("LibrarySub::getSystemService");
diff --git a/src/test/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringStaticFinalFieldTest.java b/src/test/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringStaticFinalFieldTest.java
new file mode 100644
index 0000000..6d0c21d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringStaticFinalFieldTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.identifiernamestring;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IdentifierNameStringStaticFinalFieldTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(Backend.DEX)
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addKeepRules("-identifiernamestring class * { java.lang.String CLASS_NAME; }")
+ .allowDiagnosticWarningMessages()
+ .setMinApi(AndroidApiLevel.B)
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ diagnosticMessage(
+ equalTo(
+ StringUtils.joinLines(
+ "Rule matches the static final field `java.lang.String "
+ + Main.class.getTypeName()
+ + ".CLASS_NAME`, which may have been inlined:"
+ + " -identifiernamestring class * {",
+ " java.lang.String CLASS_NAME;",
+ "}")))));
+ }
+
+ static class Main {
+
+ // @IdentifierNameString
+ public static final String CLASS_NAME = "Foo";
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java
new file mode 100644
index 0000000..15466f7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.retrace;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RetraceInlineConditionTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Throwable {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .compile()
+ .inspectProguardMap(
+ map -> {
+ // TODO(b/215339687): We should not have a rewriteFrame in the mapping file since
+ // an explicit null check should be inserted.
+ assertThat(map, CoreMatchers.containsString("com.android.tools.r8.rewriteFrame"));
+ });
+ }
+
+ static class Foo {
+
+ void inlinable(boolean loop) {
+ while (loop) {}
+ String string = toString();
+ System.out.println(string);
+ }
+ }
+
+ static class Main {
+ public static void main(String[] args) {
+ Foo foo = (args.length == 0 ? null : new Foo());
+ foo.inlinable(args.length == 0);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
new file mode 100644
index 0000000..6143522
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RetraceInlineeWithNullCheckInlinedTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public boolean throwReceiverNpe;
+
+ @Parameters(name = "{0}, throwReceiverNpe: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+ BooleanUtils.values());
+ }
+
+ public StackTrace expectedStackTrace;
+
+ @Before
+ public void setup() throws Exception {
+ // Get the expected stack trace by running on the JVM.
+ expectedStackTrace =
+ testForRuntime(parameters)
+ .addProgramClasses(Caller.class, Foo.class)
+ .run(parameters.getRuntime(), Caller.class, getArgs())
+ .getStackTrace();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Caller.class)
+ .addKeepAttributeLineNumberTable()
+ .addKeepAttributeSourceFile()
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
+ .run(parameters.getRuntime(), Caller.class, getArgs())
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(
+ (stackTrace, inspector) -> {
+ ClassSubject callerClass = inspector.clazz(Caller.class);
+ assertThat(callerClass, isPresent());
+ MethodSubject staticized = callerClass.uniqueMethodWithName("outerCaller");
+ assertThat(staticized, isPresentAndRenamed());
+ // TODO(b/214377135): The stack traces should be the same (when 206427323) is
+ // resolved.
+ assertThat(stackTrace, notIf(isSame(expectedStackTrace), throwReceiverNpe));
+ });
+ }
+
+ private String[] getArgs() {
+ return throwReceiverNpe ? new String[] {"Foo"} : new String[0];
+ }
+
+ static class Foo {
+ @NeverInline
+ @NoMethodStaticizing
+ Object notInlinable() {
+ System.out.println("Hello, world!");
+ throw new NullPointerException("Foo");
+ }
+
+ Object inlinable() {
+ return notInlinable();
+ }
+ }
+
+ static class Caller {
+
+ static void caller(Foo f) {
+ Object inlinable = f.inlinable();
+ System.out.println(inlinable);
+ }
+
+ @NeverInline
+ public static void outerCaller(Foo f) {
+ // caller should be inlined here as is. When inlinable is inlined into caller, it is done so
+ // without synthesizing a null check since the call notInlineable will throw an NPE if not
+ // staticized. We have this outer caller to check that stack traces still work correctly
+ // when the bit on throwing NPE is inlined.
+ caller(f);
+ }
+
+ public static void main(String[] args) {
+ outerCaller(args.length == 0 ? new Foo() : null);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java
index be25553..3685b5c 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java
@@ -50,8 +50,7 @@
MethodSubject checkNotNullSubject =
mainClassSubject.uniqueMethodWithName("checkNotNull");
assertThat(checkNotNullSubject, isPresent());
- // TODO(b/199864962): Allow parameter removal from check-not-null classified methods.
- assertEquals(2, checkNotNullSubject.getProgramMethod().getReference().getArity());
+ assertEquals(1, checkNotNullSubject.getProgramMethod().getReference().getArity());
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithEmptyOutput();
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
index 9a81a6a..eb1e0bf 100644
--- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -4,9 +4,10 @@
package com.android.tools.r8.regress.b69825683;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -62,19 +63,16 @@
.inspector();
List<FoundClassSubject> classes = inspector.allClasses();
-
- // Check that the synthetic class is still present when generating class files.
- assertEquals(parameters.isCfRuntime() ? 3 : 2, classes.size());
- assertEquals(
- parameters.isCfRuntime(),
- classes.stream()
- .map(FoundClassSubject::getOriginalName)
- .anyMatch(name -> name.endsWith("$1")));
+ assertEquals(2, classes.size());
+ assertThat(inspector.clazz(inner), isPresent());
+ assertThat(inspector.clazz(outer), isPresent());
}
@Test
public void innerConstructsOuter() throws Exception {
Class<?> clazz = com.android.tools.r8.regress.b69825683.innerconstructsouter.Outer.class;
+ Class<?> innerClass =
+ com.android.tools.r8.regress.b69825683.innerconstructsouter.Outer.Inner.class;
CodeInspector inspector =
testForR8(parameters.getBackend())
.addProgramFiles(ToolHelper.getClassFilesForTestPackage(clazz.getPackage()))
@@ -94,9 +92,7 @@
List<FoundClassSubject> classes = inspector.allClasses();
assertEquals(2, classes.size());
- assertTrue(
- classes.stream()
- .map(FoundClassSubject::getOriginalName)
- .noneMatch(name -> name.endsWith("$1")));
+ assertThat(inspector.clazz(clazz), isPresent());
+ assertThat(inspector.clazz(innerClass), isPresent());
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java
new file mode 100644
index 0000000..3bd714f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2022, 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.resolution;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class InvokeSuperChainTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ // This tests shows that invoke super behaves differently on V5_1_1 and V6_0_1 based on the
+ // static receiver on invoke-super, skipping the direct resolution target.
+ boolean hasIncorrectSuperInvokeSemantics =
+ parameters.isDexRuntimeVersion(Version.V5_1_1)
+ || parameters.isDexRuntimeVersion(Version.V6_0_1);
+ testForRuntime(parameters)
+ .addProgramClasses(A.class, Main.class)
+ .addProgramClassFileData(
+ rewriteBarSuperInvokeToA(B.class), rewriteBarSuperInvokeToA(C.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(
+ !hasIncorrectSuperInvokeSemantics,
+ "C::foo",
+ "B::foo",
+ "A::foo",
+ "C::bar",
+ "B::bar",
+ "A::bar")
+ .assertSuccessWithOutputLinesIf(
+ hasIncorrectSuperInvokeSemantics, "C::foo", "B::foo", "A::foo", "C::bar", "A::bar");
+ }
+
+ private byte[] rewriteBarSuperInvokeToA(Class<?> clazz) throws Exception {
+ return transformer(clazz)
+ .transformMethodInsnInMethod(
+ "bar",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (!name.equals("println")) {
+ Assert.assertEquals(Opcodes.INVOKESPECIAL, opcode);
+ Assert.assertEquals("bar", name);
+ visitor.visitMethodInsn(opcode, binaryName(A.class), name, descriptor, isInterface);
+ } else {
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform();
+ }
+
+ public static class A {
+
+ public void foo() {
+ System.out.println("A::foo");
+ }
+
+ public void bar() {
+ System.out.println("A::bar");
+ }
+ }
+
+ public static class B extends A {
+
+ @Override
+ public void foo() {
+ System.out.println("B::foo");
+ super.foo();
+ }
+
+ @Override
+ public void bar() {
+ System.out.println("B::bar");
+ super.bar();
+ }
+ }
+
+ public static class C extends B {
+
+ @Override
+ public void foo() {
+ System.out.println("C::foo");
+ super.foo();
+ }
+
+ @Override
+ public void bar() {
+ System.out.println("C::bar");
+ super.bar();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new C().foo();
+ new C().bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index 6a95824..f53c113 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -11,6 +11,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8FullTestBuilder;
@@ -19,6 +20,9 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -28,7 +32,6 @@
import com.android.tools.r8.utils.graphinspector.GraphInspector;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
@@ -269,7 +272,8 @@
.apply(this::configureHorizontalClassMerging)
.compile()
.graphInspector();
- assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector);
+ assertRetainedClassesEqual(
+ referenceInspector, ifHasMemberThenKeepClassInspector, true, true, true, true);
}
private void configureHorizontalClassMerging(R8FullTestBuilder testBuilder) {
@@ -289,14 +293,16 @@
private void assertRetainedClassesEqual(
GraphInspector referenceResult, GraphInspector conditionalResult) {
- assertRetainedClassesEqual(referenceResult, conditionalResult, false, false);
+ assertRetainedClassesEqual(referenceResult, conditionalResult, false, false, false, false);
}
private void assertRetainedClassesEqual(
GraphInspector referenceResult,
GraphInspector conditionalResult,
boolean expectReferenceIsLarger,
- boolean expectConditionalIsLarger) {
+ boolean expectReferenceIsLargerOnlyBySynthetics,
+ boolean expectConditionalIsLarger,
+ boolean expectConditionalIsLargerOnlyBySynthetics) {
Set<String> referenceClasses =
new TreeSet<>(
referenceResult.codeInspector().allClasses().stream()
@@ -308,9 +314,13 @@
.map(FoundClassSubject::getOriginalName)
.collect(Collectors.toSet());
{
- SetView<String> notInReference = Sets.difference(conditionalClasses, referenceClasses);
+ Set<String> notInReference =
+ new TreeSet<>(Sets.difference(conditionalClasses, referenceClasses));
if (expectConditionalIsLarger) {
assertFalse("Expected classes in -if rule to retain more.", notInReference.isEmpty());
+ if (expectConditionalIsLargerOnlyBySynthetics) {
+ assertAllClassesAreSynthetics(notInReference);
+ }
} else {
assertEquals(
"Classes in -if rule that are not in -keepclassmembers rule",
@@ -319,17 +329,28 @@
}
}
{
- SetView<String> notInConditional = Sets.difference(referenceClasses, conditionalClasses);
+ Set<String> notInConditional =
+ new TreeSet<>(Sets.difference(referenceClasses, conditionalClasses));
if (expectReferenceIsLarger) {
assertFalse(
"Expected classes in -keepclassmembers rule to retain more.",
notInConditional.isEmpty());
+ if (expectReferenceIsLargerOnlyBySynthetics) {
+ assertAllClassesAreSynthetics(notInConditional);
+ }
} else {
assertEquals(
"Classes in -keepclassmembers rule that are not in -if rule",
Collections.emptySet(),
- new TreeSet<>(notInConditional));
+ notInConditional);
}
}
}
+
+ private void assertAllClassesAreSynthetics(Set<String> classNames) {
+ for (String className : classNames) {
+ ClassReference classReference = Reference.classFromTypeName(className);
+ assertTrue(className, SyntheticItemsTestUtils.isExternalSynthetic(classReference));
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java b/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java
index 0040143..63e5bc9 100644
--- a/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java
@@ -34,7 +34,7 @@
dexItemFactory = program.dexItemFactory;
AppView<AppInfoWithClassHierarchy> appView = AppView.createForR8(program);
appInfo = appView.appInfo();
- subtypingInfo = new SubtypingInfo(appView);
+ subtypingInfo = SubtypingInfo.create(appView);
}
private void validateSubtype(DexType super_type, DexType sub_type) {
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
index fdea6ab..84b88e6 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.KeepConstantArguments;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoFieldTypeStrengthening;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -84,6 +85,7 @@
.enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoFieldTypeStrengtheningAnnotations()
.run(parameters.getRuntime(), KeptClass.class)
.assertSuccessWithOutputLines(EXPECTED)
.inspect(inspector -> inspect(inspector, keptForNotKept));
@@ -137,7 +139,7 @@
@NeverClassInline
public static class NotKeptClass<P> {
- public List<P> notKeptField;
+ @NoFieldTypeStrengthening public List<P> notKeptField;
@KeepConstantArguments
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForGetterTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForGetterTest.java
new file mode 100644
index 0000000..b1e3efd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForGetterTest.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2022, 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.clinit;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ClassInitInlineForGetterTest extends TestBase {
+
+ private static final String EXPECTED = "Hello World";
+ private static final String R8_RESULT = "Goodbye World";
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-keep class " + typeName(B.class) + " { <fields>; }")
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(R8_RESULT);
+ }
+
+ @NeverClassInline
+ public static class B {
+
+ public static boolean TEST = System.currentTimeMillis() == 0;
+
+ @NeverInline
+ public boolean getTest() {
+ return TEST;
+ }
+ }
+
+ public static class A {
+
+ static {
+ B.TEST = true;
+ }
+
+ @NeverInline
+ public static void triggerClassAInit(boolean b) {
+ if (b) {
+ System.out.println("Hello World");
+ } else {
+ System.out.println("Goodbye World");
+ }
+ }
+
+ public static void inlinable(B b) {
+ if (b == null) {
+ triggerClassAInit(false);
+ return;
+ }
+ triggerClassAInit(b.getTest());
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ A.inlinable(new B());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index bb46834..8cc71c8 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -122,6 +122,11 @@
return SyntheticNaming.isSynthetic(reference, null, SyntheticKind.INIT_TYPE_ARGUMENT);
}
+ public static boolean isExternalNonFixedInitializerTypeArgument(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(
+ reference, Phase.EXTERNAL, SyntheticKind.NON_FIXED_INIT_TYPE_ARGUMENT);
+ }
+
public static boolean isHorizontalInitializerTypeArgument(ClassReference reference) {
return SyntheticNaming.isSynthetic(
reference, null, SyntheticKind.HORIZONTAL_INIT_TYPE_ARGUMENT_1)
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
index bd9081e..8a0bab6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
@@ -12,6 +12,7 @@
public abstract Signature getFinalSignature();
+ @Override
public String getOriginalName() {
return getOriginalName(true);
}
diff --git a/tools/as_utils.py b/tools/as_utils.py
index 43153c6..4087137 100644
--- a/tools/as_utils.py
+++ b/tools/as_utils.py
@@ -3,11 +3,11 @@
# 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 utils
-
-from distutils.version import LooseVersion
import os
import shutil
+from distutils.version import LooseVersion
+
+import utils
if utils.is_python3():
from html.parser import HTMLParser
@@ -174,7 +174,7 @@
shutil.rmtree(dst)
elif os.path.isfile(dst):
os.remove(dst)
- os.rename(src, dst)
+ shutil.move(src, dst)
def MoveDir(src, dst, quiet=False):
assert os.path.isdir(src)
diff --git a/tools/golem_build.py b/tools/golem_build.py
index 24a1906..c691293 100755
--- a/tools/golem_build.py
+++ b/tools/golem_build.py
@@ -5,15 +5,33 @@
# Utility script to make it easier to update what golem builds.
-import gradle
import sys
+import gradle
+import retrace_benchmark
+import run_benchmark
+import run_on_app_dump
+
GRADLE_ARGS = ['--no-daemon', '-Pno_internal']
-BUILD_TARGETS = ['R8', 'D8', 'R8Lib', 'buildExampleJars',
- 'downloadAndroidCts', 'downloadDx']
+
+LEGACY_BUILD_TARGETS = [
+ 'R8',
+ 'D8',
+ 'buildExampleJars',
+ 'downloadAndroidCts',
+ 'downloadDx']
+
+def lower(items):
+ return [ item.lower() for item in items ]
def Main():
- gradle.RunGradle(GRADLE_ARGS + BUILD_TARGETS)
+ targets = set()
+ targets.update(lower(LEGACY_BUILD_TARGETS))
+ targets.update(lower(retrace_benchmark.GOLEM_BUILD_TARGETS))
+ targets.update(lower(run_benchmark.GOLEM_BUILD_TARGETS))
+ targets.update(lower(run_on_app_dump.GOLEM_BUILD_TARGETS))
+ cmd = GRADLE_ARGS + [target for target in targets]
+ gradle.RunGradle(cmd)
if __name__ == '__main__':
sys.exit(Main())
diff --git a/tools/r8_release.py b/tools/r8_release.py
index d60baa0..53a7dc9 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -324,8 +324,9 @@
def download_file(version, file, dst):
+ dir = 'raw' if len(version) != 40 else 'raw/main'
urllib.request.urlretrieve(
- ('https://storage.googleapis.com/r8-releases/raw/%s/%s' % (version, file)),
+ ('https://storage.googleapis.com/r8-releases/%s/%s/%s' % (dir, version, file)),
dst)
def download_gfile(gfile, dst):
@@ -842,8 +843,7 @@
sys.exit(1)
if args.version and not 'dev' in args.version and args.google3:
- print("You should not roll a release version into google 3")
- sys.exit(1)
+ print("WARNING: You should not roll a release version into google 3")
return args
diff --git a/tools/retrace_benchmark.py b/tools/retrace_benchmark.py
index e29bb86..42ed792 100755
--- a/tools/retrace_benchmark.py
+++ b/tools/retrace_benchmark.py
@@ -14,6 +14,7 @@
import proguard
import utils
+GOLEM_BUILD_TARGETS = ['R8Lib']
RETRACERS = ['r8', 'proguard', 'remapper']
def parse_arguments(argv):
diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py
new file mode 100755
index 0000000..2ad5200
--- /dev/null
+++ b/tools/run_benchmark.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022, 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 argparse
+import os
+import subprocess
+import sys
+
+import gradle
+import jdk
+import utils
+
+NONLIB_BUILD_TARGET = 'R8WithRelocatedDeps'
+NONLIB_TEST_BUILD_TARGETS = [utils.R8_TESTS_TARGET, utils.R8_TESTS_DEPS_TARGET]
+
+R8LIB_BUILD_TARGET = utils.R8LIB
+R8LIB_TEST_BUILD_TARGETS = [utils.R8LIB_TESTS_TARGET, utils.R8LIB_TESTS_DEPS_TARGET]
+
+# The r8lib target is always the golem target.
+GOLEM_BUILD_TARGETS = [R8LIB_BUILD_TARGET] + R8LIB_TEST_BUILD_TARGETS
+
+def get_jdk_home(options, benchmark):
+ if options.golem:
+ return os.path.join('benchmarks', benchmark, 'linux')
+ return None
+
+def parse_options(argv):
+ result = argparse.ArgumentParser(description = 'Run test-based benchmarks.')
+ result.add_argument('--golem',
+ help='Indicate this as a run on golem',
+ default=False,
+ action='store_true')
+ result.add_argument('--benchmark',
+ help='The test benchmark to run',
+ required=True)
+ result.add_argument('--target',
+ help='The test target to run',
+ required=True,
+ # These should 1:1 with BenchmarkTarget.java
+ choices=['d8', 'r8', 'r8-force', 'r8-compat'])
+ result.add_argument('--nolib', '--no-lib', '--no-r8lib',
+ help='Run the non-lib R8 build (default false)',
+ default=False,
+ action='store_true')
+ result.add_argument('--no-build', '--no_build',
+ help='Run without building first (default false)',
+ default=False,
+ action='store_true')
+ result.add_argument('--enable-assertions', '--enable_assertions', '-ea',
+ help='Enable assertions when running',
+ default=False,
+ action='store_true')
+ result.add_argument('--print-times',
+ help='Print timing information from r8',
+ default=False,
+ action='store_true')
+ result.add_argument('--temp',
+ help='A directory to use for temporaries and outputs.',
+ default=None)
+ return result.parse_known_args(argv)
+
+def main(argv):
+ (options, args) = parse_options(argv)
+
+ if options.golem:
+ options.no_build = True
+ if options.nolib:
+ print("Error: golem should always run r8lib")
+ return 1
+
+ if options.nolib:
+ buildTargets = [NONLIB_BUILD_TARGET] + NONLIB_TEST_BUILD_TARGETS
+ r8jar = utils.R8_WITH_RELOCATED_DEPS_JAR
+ testjars = [utils.R8_TESTS_DEPS_JAR, utils.R8_TESTS_JAR]
+ else:
+ buildTargets = GOLEM_BUILD_TARGETS
+ r8jar = utils.R8LIB_JAR
+ testjars = [utils.R8LIB_TESTS_DEPS_JAR, utils.R8LIB_TESTS_JAR]
+
+ if not options.no_build:
+ gradle.RunGradle(buildTargets + ['-Pno_internal'])
+
+ return run(options, r8jar, testjars)
+
+def run(options, r8jar, testjars):
+ jdkhome = get_jdk_home(options, options.benchmark)
+ cmd = [jdk.GetJavaExecutable(jdkhome)]
+ if options.enable_assertions:
+ cmd.append('-ea')
+ if options.print_times:
+ cmd.append('-Dcom.android.tools.r8.printtimes=1')
+ cmd.extend(['-cp', ':'.join([r8jar] + testjars)])
+ cmd.extend([
+ 'com.android.tools.r8.benchmarks.BenchmarkMainEntryRunner',
+ options.benchmark,
+ options.target,
+ ])
+ return subprocess.check_call(cmd)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index c55a6f7..6035752 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -3,24 +3,25 @@
# 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 argparse
+import hashlib
+import os
+import shutil
+import sys
+import time
+import zipfile
+from datetime import datetime
+
import adb
import apk_masseur
import as_utils
import compiledump
import gradle
-import hashlib
import jdk
-import argparse
-import os
-import shutil
-import sys
-import time
import update_prebuilds_in_android
import utils
-import zipfile
-from datetime import datetime
-
+GOLEM_BUILD_TARGETS = ['R8Lib', 'R8Retrace']
SHRINKERS = ['r8', 'r8-full', 'r8-nolib', 'r8-nolib-full']
class AttrDict(dict):
@@ -1060,9 +1061,7 @@
print_indented('final targetsFull = ["R8-full-minify-optimize-shrink"];', 2)
# Avoid calculating this for every app
jdk_gz = jdk.GetJdkHome() + '.tar.gz'
- download_sha(jdk_gz + '.sha1', False, quiet=True)
- jdk_sha256 = get_sha256(jdk_gz)
- add_golem_resource(2, jdk_gz, 'openjdk', sha256=jdk_sha256)
+ add_golem_resource(2, jdk_gz, 'openjdk')
for app in options.apps:
if app.folder and not app.internal:
indentation = 2;
@@ -1171,7 +1170,7 @@
os.path.join(temp_dir, target), os.path.join(temp_dir, 'r8lib.jar'),
quiet=options.quiet)
elif options.version == 'main':
- if not (options.no_build or options.golem):
+ if not options.no_build:
gradle.RunGradle(['R8Retrace', 'r8', '-Pno_internal'])
build_r8lib = False
for shrinker in options.shrinker:
diff --git a/tools/test.py b/tools/test.py
index 041f17b..9d5e99f 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -118,7 +118,7 @@
help='Use a custom java version to run tests.')
result.add_option('--java-max-memory-size', '--java_max_memory_size',
help='Set memory for running tests, default 4G',
- default='4G')
+ default=os.environ.get('R8_JAVA_MAX_MEMORY_SIZE', '4G'))
result.add_option('--test-namespace', '--test_namespace',
help='Only run tests in this namespace. The namespace is relative to '
'com/android/tools/r8, e.g., desugar/desugaredlibrary',
diff --git a/tools/utils.py b/tools/utils.py
index 5d574ac..eb7849b 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -45,10 +45,15 @@
R8RETRACE_NO_DEPS = 'R8RetraceNoDeps'
R8_SRC = 'sourceJar'
LIBRARY_DESUGAR_CONVERSIONS = 'buildLibraryDesugarConversions'
+R8_TESTS_TARGET = 'TestJar'
+R8_TESTS_DEPS_TARGET = 'RepackageTestDeps'
+R8LIB_TESTS_TARGET = 'configureTestForR8Lib'
+R8LIB_TESTS_DEPS_TARGET = R8_TESTS_DEPS_TARGET
ALL_DEPS_JAR = os.path.join(LIBS, 'deps_all.jar')
D8_JAR = os.path.join(LIBS, 'd8.jar')
R8_JAR = os.path.join(LIBS, 'r8.jar')
+R8_WITH_RELOCATED_DEPS_JAR = os.path.join(LIBS, 'r8_with_relocated_deps.jar')
R8LIB_JAR = os.path.join(LIBS, 'r8lib.jar')
R8LIB_MAP = os.path.join(LIBS, 'r8lib.jar.map')
R8_SRC_JAR = os.path.join(LIBS, 'r8-src.jar')
@@ -56,6 +61,10 @@
R8_FULL_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-full-exclude-deps.jar')
R8RETRACE_JAR = os.path.join(LIBS, 'r8retrace.jar')
R8RETRACE_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8retrace-exclude-deps.jar')
+R8_TESTS_JAR = os.path.join(LIBS, 'r8tests.jar')
+R8LIB_TESTS_JAR = os.path.join(LIBS, 'r8libtestdeps-cf.jar')
+R8_TESTS_DEPS_JAR = os.path.join(LIBS, 'test_deps_all.jar')
+R8LIB_TESTS_DEPS_JAR = R8_TESTS_DEPS_JAR
MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
MAVEN_ZIP_LIB = os.path.join(LIBS, 'r8lib.zip')
LIBRARY_DESUGAR_CONVERSIONS_ZIP = os.path.join(LIBS, 'library_desugar_conversions.zip')
@@ -395,7 +404,9 @@
tar.extractall(path=dirname)
def check_gcert():
- subprocess.check_call(['gcert'])
+ status = subprocess.call(['gcertstatus'])
+ if status != 0:
+ subprocess.check_call(['gcert'])
# Note that gcs is eventually consistent with regards to list operations.
# This is not a problem in our case, but don't ever use this method