Merge "Introduce IRCode#entryBlock."
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index e1be490..db83f6a 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -10,6 +10,7 @@
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -22,6 +23,10 @@
public final DexItemFactory dexItemFactory;
private final ConcurrentHashMap<DexType, Map<Descriptor<?,?>, KeyedDexItem<?>>> definitions =
new ConcurrentHashMap<>();
+ // For some optimizations, e.g. optimizing synthetic classes, we may need to resolve the current
+ // class being optimized.
+ private ConcurrentHashMap<DexType, DexProgramClass> synthesizedClasses =
+ new ConcurrentHashMap<>();
public AppInfo(DexApplication application) {
this.app = application;
@@ -42,6 +47,16 @@
this(application);
}
+ public void addSynthesizedClass(DexProgramClass clazz) {
+ assert clazz.type.isD8R8SynthesizedClassType();
+ DexProgramClass previous = synthesizedClasses.put(clazz.type, clazz);
+ assert previous == null || previous == clazz;
+ }
+
+ public Collection<DexProgramClass> getSynthesizedClassesForSanityCheck() {
+ return Collections.unmodifiableCollection(synthesizedClasses.values());
+ }
+
private Map<Descriptor<?,?>, KeyedDexItem<?>> computeDefinitions(DexType type) {
Builder<Descriptor<?,?>, KeyedDexItem<?>> builder = ImmutableMap.builder();
DexClass clazz = app.definitionFor(type);
@@ -72,6 +87,11 @@
}
public DexClass definitionFor(DexType type) {
+ DexProgramClass cached = synthesizedClasses.get(type);
+ if (cached != null) {
+ assert app.definitionFor(type) == null;
+ return cached;
+ }
return app.definitionFor(type);
}
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 0efc380..96f34bc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -114,7 +114,7 @@
// The only way to figure out whether the DexValue contains the final value
// is ensure the value is not the default or check <clinit> is not present.
boolean isEffectivelyFinal =
- (accessFlags.isFinal() || !appInfo.fieldsWritten.contains(field))
+ (accessFlags.isFinal() || !appInfo.isFieldWritten(field))
&& !appInfo.isPinned(field);
if (!isEffectivelyFinal) {
return null;
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 2ebccfe..249a2b5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -10,7 +10,8 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.ir.desugar.Java8MethodRewriter;
+import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
@@ -221,14 +222,6 @@
}
while (other.hierarchyLevel < self.hierarchyLevel) {
DexClass holder = appInfo.definitionFor(self);
- // TODO(b/113374256): even synthesized class should be available ATM.
- if (holder == null) {
- assert self.isD8R8SynthesizedClassType();
- if (Log.ENABLED) {
- Log.debug(getClass(), "%s is not in AppInfo yet.", self.toSourceString());
- }
- return orElse;
- }
assert holder != null && !holder.isInterface();
self = holder.superType;
}
@@ -483,7 +476,9 @@
|| name.contains(DISPATCH_CLASS_NAME_SUFFIX)
|| name.contains(LAMBDA_CLASS_NAME_PREFIX)
|| name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX)
- || name.contains(OutlineOptions.CLASS_NAME);
+ || name.contains(OutlineOptions.CLASS_NAME)
+ || name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME)
+ || name.contains(Java8MethodRewriter.UTILITY_CLASS_NAME_PREFIX);
}
public int elementSizeForPrimitiveArrayType() {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 5fd473f..b964dd3 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -388,9 +388,7 @@
public abstract DexMethod getRenamedMethodSignature(DexMethod originalMethod);
public DexEncodedMethod mapDexEncodedMethod(
- DexEncodedMethod originalEncodedMethod,
- AppInfo appInfo,
- Map<DexType, DexProgramClass> synthesizedClasses) {
+ DexEncodedMethod originalEncodedMethod, AppInfo appInfo) {
DexMethod newMethod = getRenamedMethodSignature(originalEncodedMethod.method);
// Note that:
// * Even if `newMethod` is the same as `originalEncodedMethod.method`, we still need to look it
@@ -398,14 +396,7 @@
// * We can't directly use AppInfo#definitionFor(DexMethod) since definitions may not be
// updated either yet.
DexClass newHolder = appInfo.definitionFor(newMethod.holder);
-
- // TODO(b/120130831): Need to ensure that all synthesized classes are part of the application.
- if (newHolder == null) {
- newHolder = synthesizedClasses.get(newMethod.holder);
- }
-
assert newHolder != null;
-
DexEncodedMethod newEncodedMethod = newHolder.lookupMethod(newMethod);
assert newEncodedMethod != null;
return newEncodedMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index cb7a5d5..ff6abd3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -89,12 +89,10 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -149,10 +147,6 @@
private final OptimizationFeedback simpleOptimizationFeedback = new OptimizationFeedbackSimple();
private DexString highestSortingString;
- // For some optimizations, e.g. optimizing synthetic classes, we may need to resolve
- // the current class being optimized.
- private ConcurrentHashMap<DexType, DexProgramClass> cachedClasses = new ConcurrentHashMap<>();
-
// The argument `appView` is only available when full program optimizations are allowed
// (i.e., when running R8).
private IRConverter(
@@ -352,30 +346,24 @@
InterfaceMethodRewriter.Flavor includeAllResources,
ExecutorService executorService)
throws ExecutionException {
- desugarInterfaceMethods(builder, includeAllResources, executorService, null);
- }
-
- private void desugarInterfaceMethods(
- Builder<?> builder,
- InterfaceMethodRewriter.Flavor includeAllResources,
- ExecutorService executorService,
- Map<DexType, DexProgramClass> synthesizedClasses)
- throws ExecutionException {
if (interfaceMethodRewriter != null) {
interfaceMethodRewriter.desugarInterfaceMethods(
- builder, includeAllResources, executorService, synthesizedClasses);
+ builder, includeAllResources, executorService);
}
}
- private void synthesizeTwrCloseResourceUtilityClass(Builder<?> builder) {
+ private void synthesizeTwrCloseResourceUtilityClass(
+ Builder<?> builder, ExecutorService executorService)
+ throws ExecutionException {
if (twrCloseResourceRewriter != null) {
- twrCloseResourceRewriter.synthesizeUtilityClass(builder, options);
+ twrCloseResourceRewriter.synthesizeUtilityClass(builder, executorService, options);
}
}
- private void synthesizeJava8UtilityClass(Builder<?> builder) {
+ private void synthesizeJava8UtilityClass(
+ Builder<?> builder, ExecutorService executorService) throws ExecutionException {
if (java8MethodRewriter != null) {
- java8MethodRewriter.synthesizeUtilityClass(builder, options);
+ java8MethodRewriter.synthesizeUtilityClass(builder, executorService, options);
}
}
@@ -398,8 +386,8 @@
synthesizeLambdaClasses(builder, executor);
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
- synthesizeTwrCloseResourceUtilityClass(builder);
- synthesizeJava8UtilityClass(builder);
+ synthesizeTwrCloseResourceUtilityClass(builder, executor);
+ synthesizeJava8UtilityClass(builder, executor);
processCovariantReturnTypeAnnotations(builder);
handleSynthesizedClassMapping(builder);
@@ -590,21 +578,20 @@
synthesizeLambdaClasses(builder, executorService);
printPhase("Interface method desugaring");
- Map<DexType, DexProgramClass> synthesizedClasses = new IdentityHashMap<>();
- desugarInterfaceMethods(builder, IncludeAllResources, executorService, synthesizedClasses);
+ desugarInterfaceMethods(builder, IncludeAllResources, executorService);
printPhase("Twr close resource utility class synthesis");
- synthesizeTwrCloseResourceUtilityClass(builder);
- synthesizeJava8UtilityClass(builder);
+ synthesizeTwrCloseResourceUtilityClass(builder, executorService);
+ synthesizeJava8UtilityClass(builder, executorService);
handleSynthesizedClassMapping(builder);
printPhase("Lambda merging finalization");
- finalizeLambdaMerging(application, feedback, builder, executorService, synthesizedClasses);
+ finalizeLambdaMerging(application, feedback, builder, executorService);
if (outliner != null) {
printPhase("Outlining");
timing.begin("IR conversion phase 2");
- if (outliner.selectMethodsForOutlining(synthesizedClasses)) {
+ if (outliner.selectMethodsForOutlining()) {
forEachSelectedOutliningMethod(
executorService,
(code, method) -> {
@@ -612,7 +599,8 @@
outliner.identifyOutlineSites(code, method);
});
DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
- optimizeSynthesizedClass(outlineClass);
+ appInfo.addSynthesizedClass(outlineClass);
+ optimizeSynthesizedClass(outlineClass, executorService);
forEachSelectedOutliningMethod(
executorService,
(code, method) -> {
@@ -636,6 +624,12 @@
uninstantiatedTypeOptimization.logResults();
}
+ // Check if what we've added to the application builder as synthesized classes are same as
+ // what we've added and used through AppInfo.
+ assert appInfo.getSynthesizedClassesForSanityCheck()
+ .containsAll(builder.getSynthesizedClasses())
+ && builder.getSynthesizedClasses()
+ .containsAll(appInfo.getSynthesizedClassesForSanityCheck());
return builder.build();
}
@@ -687,14 +681,13 @@
private void finalizeLambdaMerging(
DexApplication application,
- OptimizationFeedback directFeedback,
+ OptimizationFeedback feedback,
Builder<?> builder,
- ExecutorService executorService,
- Map<DexType, DexProgramClass> synthesizedClasses)
+ ExecutorService executorService)
throws ExecutionException {
if (lambdaMerger != null) {
lambdaMerger.applyLambdaClassMapping(
- application, this, directFeedback, builder, executorService, synthesizedClasses);
+ application, this, feedback, builder, executorService);
}
}
@@ -744,49 +737,24 @@
return result;
}
- public DexClass definitionFor(DexType type) {
- DexProgramClass cached = cachedClasses.get(type);
- return cached != null ? cached : appInfo.definitionFor(type);
- }
-
- public void optimizeSynthesizedClass(DexProgramClass clazz) {
- try {
- enterCachedClass(clazz);
- // Process the generated class, but don't apply any outlining.
- clazz.forEachMethod(this::optimizeSynthesizedMethod);
- } finally {
- leaveCachedClass(clazz);
- }
+ public void optimizeSynthesizedClass(
+ DexProgramClass clazz, ExecutorService executorService)
+ throws ExecutionException {
+ Set<DexEncodedMethod> methods = Sets.newIdentityHashSet();
+ clazz.forEachMethod(methods::add);
+ // Process the generated class, but don't apply any outlining.
+ optimizeSynthesizedMethodsConcurrently(methods, executorService);
}
public void optimizeSynthesizedClasses(
Collection<DexProgramClass> classes, ExecutorService executorService)
throws ExecutionException {
Set<DexEncodedMethod> methods = Sets.newIdentityHashSet();
- try {
- for (DexProgramClass clazz : classes) {
- enterCachedClass(clazz);
- clazz.forEachMethod(methods::add);
- }
- // Process the generated class, but don't apply any outlining.
- optimizeSynthesizedMethods(methods, executorService);
- } finally {
- for (DexProgramClass clazz : classes) {
- leaveCachedClass(clazz);
- }
+ for (DexProgramClass clazz : classes) {
+ clazz.forEachMethod(methods::add);
}
- }
-
- public void optimizeMethodOnSynthesizedClass(DexProgramClass clazz, DexEncodedMethod method) {
- if (!method.isProcessed()) {
- try {
- enterCachedClass(clazz);
- // Process the generated method, but don't apply any outlining.
- optimizeSynthesizedMethod(method);
- } finally {
- leaveCachedClass(clazz);
- }
- }
+ // Process the generated class, but don't apply any outlining.
+ optimizeSynthesizedMethodsConcurrently(methods, executorService);
}
public void optimizeSynthesizedMethod(DexEncodedMethod method) {
@@ -801,7 +769,7 @@
}
}
- public void optimizeSynthesizedMethods(
+ public void optimizeSynthesizedMethodsConcurrently(
Collection<DexEncodedMethod> methods, ExecutorService executorService)
throws ExecutionException {
List<Future<?>> futures = new ArrayList<>();
@@ -821,16 +789,6 @@
ThreadUtils.awaitFutures(futures);
}
- private void enterCachedClass(DexProgramClass clazz) {
- DexProgramClass previous = cachedClasses.put(clazz.type, clazz);
- assert previous == null;
- }
-
- private void leaveCachedClass(DexProgramClass clazz) {
- DexProgramClass existing = cachedClasses.remove(clazz.type);
- assert existing == clazz;
- }
-
private String logCode(InternalOptions options, DexEncodedMethod method) {
return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
}
@@ -1198,7 +1156,8 @@
// original method signature (this could have changed as a result of, for example, class
// merging). Then, we find the type that now corresponds to the the original holder.
DexMethod originalSignature = graphLense().getOriginalMethodSignature(method.method);
- DexClass originalHolder = definitionFor(graphLense().lookupType(originalSignature.holder));
+ DexClass originalHolder = appInfo.definitionFor(
+ graphLense().lookupType(originalSignature.holder));
if (originalHolder.hasKotlinInfo()) {
KotlinInfo kotlinInfo = originalHolder.getKotlinInfo();
if (kotlinInfo.hasNonNullParameterHints()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index ec788a4..401feaa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -60,7 +60,7 @@
DexType superType = clazz.superType;
// If superClass definition is missing, just skip this part and let real processing of its
// subclasses report the error if it is required.
- DexClass superClass = superType == null ? null : rewriter.findDefinitionFor(superType);
+ DexClass superClass = superType == null ? null : rewriter.appInfo.definitionFor(superType);
if (superClass != null && superType != rewriter.factory.objectType) {
if (superClass.isInterface()) {
throw new CompilationError("Interface `" + superClass.toSourceString()
@@ -96,7 +96,7 @@
private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) {
DexMethod method = defaultMethod.method;
- DexClass target = rewriter.findDefinitionFor(method.holder);
+ DexClass target = rewriter.appInfo.definitionFor(method.holder);
// NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
// even if this results in invalid code, these classes are never desugared.
assert target != null && !target.isLibraryClass();
@@ -168,7 +168,7 @@
if (current.superType == null) {
break;
} else {
- DexClass superClass = rewriter.findDefinitionFor(current.superType);
+ DexClass superClass = rewriter.appInfo.definitionFor(current.superType);
if (superClass != null) {
current = superClass;
} else {
@@ -206,7 +206,7 @@
DexType superType = current.superType;
DexClass superClass = null;
if (superType != null) {
- superClass = rewriter.findDefinitionFor(superType);
+ superClass = rewriter.appInfo.definitionFor(superType);
// It's available or we would have failed while analyzing the hierarchy for interfaces.
assert superClass != null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index e79017d..abd38a5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication.Builder;
@@ -80,6 +81,7 @@
public static final String PRIVATE_METHOD_PREFIX = "$private$";
private final AppView<? extends AppInfoWithLiveness> appView;
+ final AppInfo appInfo;
private final IRConverter converter;
private final InternalOptions options;
final DexItemFactory factory;
@@ -121,6 +123,7 @@
assert converter != null;
this.appView = appView;
this.converter = converter;
+ this.appInfo = converter.appInfo;
this.options = options;
this.factory = options.itemFactory;
}
@@ -156,7 +159,7 @@
if (instruction.isInvokeStatic()) {
InvokeStatic invokeStatic = instruction.asInvokeStatic();
DexMethod method = invokeStatic.getInvokedMethod();
- DexClass clazz = findDefinitionFor(method.holder);
+ DexClass clazz = appInfo.definitionFor(method.holder);
if (Java8MethodRewriter.hasJava8MethodRewritePrefix(method.holder)) {
// We did not create this code yet, but it will not require rewriting.
continue;
@@ -187,7 +190,7 @@
invokeStatic.outValue(), invokeStatic.arguments()));
requiredDispatchClasses
.computeIfAbsent(clazz.asLibraryClass(), k -> Sets.newConcurrentHashSet())
- .add(findDefinitionFor(encodedMethod.method.holder).asProgramClass());
+ .add(appInfo.definitionFor(encodedMethod.method.holder).asProgramClass());
}
} else {
instructions.replaceCurrentInstruction(
@@ -201,7 +204,7 @@
if (instruction.isInvokeSuper()) {
InvokeSuper invokeSuper = instruction.asInvokeSuper();
DexMethod method = invokeSuper.getInvokedMethod();
- DexClass clazz = findDefinitionFor(method.holder);
+ DexClass clazz = appInfo.definitionFor(method.holder);
if (clazz == null) {
// NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
// exception but we can not report it as error since it can also be the intended
@@ -218,7 +221,7 @@
// WARNING: This may result in incorrect code on older platforms!
// Retarget call to an appropriate method of companion class.
DexMethod amendedMethod = amendDefaultMethod(
- findDefinitionFor(encodedMethod.method.holder), method);
+ appInfo.definitionFor(encodedMethod.method.holder), method);
instructions.replaceCurrentInstruction(
new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
invokeSuper.outValue(), invokeSuper.arguments()));
@@ -233,7 +236,7 @@
continue;
}
- DexClass clazz = findDefinitionFor(method.holder);
+ DexClass clazz = appInfo.definitionFor(method.holder);
if (clazz == null) {
// Report missing class since we don't know if it is an interface.
warnMissingType(encodedMethod.method, method.holder);
@@ -279,7 +282,7 @@
private void reportStaticInterfaceMethodHandle(DexMethod referencedFrom, DexMethodHandle handle) {
if (handle.type.isInvokeStatic()) {
- DexClass holderClass = findDefinitionFor(handle.asMethod().holder);
+ DexClass holderClass = appInfo.definitionFor(handle.asMethod().holder);
// NOTE: If the class definition is missing we can't check. Let it be handled as any other
// missing call target.
if (holderClass == null) {
@@ -292,15 +295,6 @@
}
}
- /**
- * Returns the class definition for the specified type.
- *
- * @return may return null if no definition for the given type is available.
- */
- final DexClass findDefinitionFor(DexType type) {
- return converter.definitionFor(type);
- }
-
// Gets the companion class for the interface `type`.
final DexType getCompanionClassType(DexType type) {
assert type.isClassType();
@@ -334,7 +328,7 @@
}
private boolean isInMainDexList(DexType iface) {
- return converter.appInfo.isInMainDexList(iface);
+ return appInfo.isInMainDexList(iface);
}
// Represent a static interface method as a method of companion class.
@@ -393,8 +387,7 @@
public void desugarInterfaceMethods(
Builder<?> builder,
Flavor flavour,
- ExecutorService executorService,
- Map<DexType, DexProgramClass> synthesizedClasses)
+ ExecutorService executorService)
throws ExecutionException {
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
@@ -409,13 +402,10 @@
// are just moved from interfaces and don't need to be re-processed.
DexProgramClass synthesizedClass = entry.getValue();
builder.addSynthesizedClass(synthesizedClass, isInMainDexList(entry.getKey()));
-
- if (synthesizedClasses != null) {
- synthesizedClasses.put(synthesizedClass.type, synthesizedClass);
- }
+ appInfo.addSynthesizedClass(synthesizedClass);
}
- converter.optimizeSynthesizedMethods(synthesizedMethods, executorService);
+ converter.optimizeSynthesizedMethodsConcurrently(synthesizedMethods, executorService);
// Cached data is not needed any more.
clear();
@@ -528,7 +518,7 @@
if (isCompanionClassType(holder)) {
holder = getInterfaceClassType(holder);
}
- DexClass clazz = converter.appInfo.definitionFor(holder);
+ DexClass clazz = appInfo.definitionFor(holder);
return clazz == null ? Origin.unknown() : clazz.getOrigin();
}
@@ -550,7 +540,7 @@
DexClass implementing,
DexType iface) {
DefaultMethodsHelper helper = new DefaultMethodsHelper();
- DexClass definedInterface = findDefinitionFor(iface);
+ DexClass definedInterface = appInfo.definitionFor(iface);
if (definedInterface == null) {
warnMissingInterface(classToDesugar, implementing, iface);
return helper.wrapInCollection();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 054b950..e4d4b01 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -322,7 +322,7 @@
if (!seenBefore.add(superType)) {
continue;
}
- DexClass clazz = rewriter.findDefinitionFor(superType);
+ DexClass clazz = rewriter.appInfo.definitionFor(superType);
if (clazz != null) {
if (clazz.lookupVirtualMethod(method.method) != null) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
index 9e2e9ab..e930bcf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
@@ -37,9 +37,12 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.BiFunction;
public final class Java8MethodRewriter {
+ public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$java8methods$utility";
private static final String UTILITY_CLASS_DESCRIPTOR_PREFIX = "L$r8$java8methods$utility";
private final Set<DexType> holders = Sets.newConcurrentHashSet();
private final IRConverter converter;
@@ -88,7 +91,9 @@
return clazz.descriptor.toString().startsWith(UTILITY_CLASS_DESCRIPTOR_PREFIX);
}
- public void synthesizeUtilityClass(Builder<?> builder, InternalOptions options) {
+ public void synthesizeUtilityClass(
+ Builder<?> builder, ExecutorService executorService, InternalOptions options)
+ throws ExecutionException {
if (holders.isEmpty()) {
return;
}
@@ -134,7 +139,8 @@
code.setUpContext(utilityClass);
boolean addToMainDexList = referencingClasses.stream()
.anyMatch(clazz -> converter.appInfo.isInMainDexList(clazz.type));
- converter.optimizeSynthesizedClass(utilityClass);
+ converter.appInfo.addSynthesizedClass(utilityClass);
+ converter.optimizeSynthesizedClass(utilityClass, executorService);
builder.addSynthesizedClass(utilityClass, addToMainDexList);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 865a62c..cff5392 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -153,8 +153,9 @@
synthesizeVirtualMethods(mainMethod),
rewriter.factory.getSkipNameValidationForTesting());
// Optimize main method.
- rewriter.converter.optimizeMethodOnSynthesizedClass(
- clazz, clazz.lookupVirtualMethod(mainMethod));
+ rewriter.converter.appInfo.addSynthesizedClass(clazz);
+ rewriter.converter.optimizeSynthesizedMethod(clazz.lookupVirtualMethod(mainMethod));
+
// The method addSynthesizedFrom() may be called concurrently. To avoid a Concurrent-
// ModificationException we must use synchronization.
synchronized (synthesizedFrom) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 4e29e6f..a1bb373 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -181,14 +181,15 @@
/** Generates lambda classes and adds them to the builder. */
public void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
throws ExecutionException {
+ for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
+ DexProgramClass synthesizedClass = lambdaClass.getLambdaClass();
+ appInfo.addSynthesizedClass(synthesizedClass);
+ builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
+ }
converter.optimizeSynthesizedClasses(
knownLambdaClasses.values().stream()
.map(LambdaClass::getLambdaClass).collect(ImmutableSet.toImmutableSet()),
executorService);
- for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
- DexProgramClass synthesizedClass = lambdaClass.getLambdaClass();
- builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
- }
}
public Set<DexCallSite> getDesugaredCallSites() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 568e021..965d44e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -31,6 +31,8 @@
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
// Try with resources outlining processor. Handles $closeResource methods
// synthesized by java 9 compiler.
@@ -45,6 +47,7 @@
// tree shaking to remove them since now they should not be referenced.
//
public final class TwrCloseResourceRewriter {
+ public static final String UTILITY_CLASS_NAME = "$r8$twr$utility";
public static final String UTILITY_CLASS_DESCRIPTOR = "L$r8$twr$utility;";
private final IRConverter converter;
@@ -107,7 +110,9 @@
&& original.proto == converter.appInfo.dexItemFactory.twrCloseResourceMethodProto;
}
- public void synthesizeUtilityClass(Builder<?> builder, InternalOptions options) {
+ public void synthesizeUtilityClass(
+ Builder<?> builder, ExecutorService executorService, InternalOptions options)
+ throws ExecutionException {
if (referencingClasses.isEmpty()) {
return;
}
@@ -144,7 +149,8 @@
// Process created class and method.
boolean addToMainDexList = referencingClasses.stream()
.anyMatch(clazz -> converter.appInfo.isInMainDexList(clazz.type));
- converter.optimizeSynthesizedClass(utilityClass);
+ converter.appInfo.addSynthesizedClass(utilityClass);
+ converter.optimizeSynthesizedClass(utilityClass, executorService);
builder.addSynthesizedClass(utilityClass, addToMainDexList);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index faf7bbe..b5f3a2d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1886,7 +1886,7 @@
return;
}
- DexClass clazz = definitionFor(method.method.getHolder());
+ DexClass clazz = appInfo.definitionFor(method.method.getHolder());
if (clazz == null) {
return;
}
@@ -2087,10 +2087,6 @@
}
}
- DexClass definitionFor(DexType type) {
- return converter.definitionFor(type);
- }
-
public void removeTrivialCheckCastAndInstanceOfInstructions(
IRCode code, boolean enableWholeProgramOptimizations) {
if (!enableWholeProgramOptimizations) {
@@ -2196,7 +2192,7 @@
if (baseType.isPrimitiveType()) {
return false;
}
- DexClass clazz = definitionFor(baseType);
+ DexClass clazz = appInfo.definitionFor(baseType);
if (clazz == null) {
// Conservatively say yes.
return true;
@@ -3874,7 +3870,7 @@
if (type == dexItemFactory.throwableType) {
return true;
}
- DexClass dexClass = definitionFor(type);
+ DexClass dexClass = appInfo.definitionFor(type);
if (dexClass == null) {
throw new CompilationError("Class or interface " + type.toSourceString() +
" required for desugaring of try-with-resources is not found.");
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 2c14153..22c3da8 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
@@ -286,7 +286,7 @@
? appInfo.lookupInstanceTarget(field.getHolder(), field)
: appInfo.lookupStaticTarget(field.getHolder(), field);
// TODO(b/123857022): Should be possible to use `!isFieldRead(field)`.
- if (target != null && !isFieldRead(target.field)) {
+ if (target != null && !appInfo.isFieldRead(target.field)) {
// Remove writes to dead (i.e. never read) fields.
iterator.removeOrReplaceByDebugLocalRead();
}
@@ -327,17 +327,4 @@
}
assert code.isConsistentSSA();
}
-
- private boolean isFieldRead(DexField field) {
- return appInfo.fieldsRead.contains(field)
- // TODO(b/121354886): Pinned fields should be in `fieldsRead`.
- || appInfo.isPinned(field)
- // For library classes we don't know whether a field is read.
- || isLibraryField(field);
- }
-
- private boolean isLibraryField(DexField field) {
- DexClass holder = appInfo.definitionFor(field.clazz);
- return holder == null || holder.isLibraryClass();
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 73f2a91..27ec7dc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1232,7 +1232,7 @@
}
}
- public boolean selectMethodsForOutlining(Map<DexType, DexProgramClass> synthesizedClasses) {
+ public boolean selectMethodsForOutlining() {
assert methodsSelectedForOutlining.size() == 0;
assert outlineSites.size() == 0;
for (List<DexEncodedMethod> outlineMethods : candidateMethodLists) {
@@ -1241,7 +1241,7 @@
methodsSelectedForOutlining.add(
converter
.graphLense()
- .mapDexEncodedMethod(outlineMethod, appInfo, synthesizedClasses));
+ .mapDexEncodedMethod(outlineMethod, appInfo));
}
}
}
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 1f036b6..a0ba213 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
@@ -117,12 +117,14 @@
if (!eligibleClass.isClassType()) {
return false;
}
- eligibleClassDefinition = appInfo.definitionFor(eligibleClass);
- if (eligibleClassDefinition == null && lambdaRewriter != null) {
+ if (lambdaRewriter != null) {
// Check if the class is synthesized for a desugared lambda
eligibleClassDefinition = lambdaRewriter.getLambdaClass(eligibleClass);
isDesugaredLambda = eligibleClassDefinition != null;
}
+ if (eligibleClassDefinition == null) {
+ eligibleClassDefinition = appInfo.definitionFor(eligibleClass);
+ }
return eligibleClassDefinition != null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index b988a89..ab6f635 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.ir.conversion.CallSiteInformation;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.Outliner;
import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
@@ -36,6 +37,7 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
+import com.google.common.base.Predicates;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.IdentityHashMap;
@@ -194,8 +196,7 @@
IRConverter converter,
OptimizationFeedback feedback,
Builder<?> builder,
- ExecutorService executorService,
- Map<DexType, DexProgramClass> synthesizedClasses)
+ ExecutorService executorService)
throws ExecutionException {
if (lambdas.isEmpty()) {
return;
@@ -218,18 +219,29 @@
this.strategyFactory = ApplyStrategy::new;
// Add synthesized lambda group classes to the builder.
- converter.optimizeSynthesizedClasses(lambdaGroupsClasses.values(), executorService);
for (Entry<LambdaGroup, DexProgramClass> entry : lambdaGroupsClasses.entrySet()) {
DexProgramClass synthesizedClass = entry.getValue();
- synthesizedClasses.put(synthesizedClass.type, synthesizedClass);
+ converter.appInfo.addSynthesizedClass(synthesizedClass);
builder.addSynthesizedClass(
synthesizedClass, entry.getKey().shouldAddToMainDex(converter.appInfo));
+ // Eventually, we need to process synthesized methods in the lambda group.
+ // Otherwise, abstract SynthesizedCode will be flown to Enqueuer.
+ // But that process should not see the holder. Otherwise, lambda calls in the main dispatch
+ // method became recursive calls via the lense rewriter. They should remain, then inliner
+ // will inline methods from mergee lambdas to the main dispatch method.
+ // Then, there is a dilemma: other sub optimizations trigger subtype lookup that will throw
+ // NPE if it cannot find the holder for this synthesized lambda group.
+ // One hack here is to mark those methods `processed` so that the lense rewriter is skipped.
+ synthesizedClass.forEachMethod(encodedMethod -> {
+ encodedMethod.markProcessed(ConstraintWithTarget.NEVER);
+ });
}
+ converter.optimizeSynthesizedClasses(lambdaGroupsClasses.values(), executorService);
// Rewrite lambda class references into lambda group class
// references inside methods from the processing queue.
- rewriteLambdaReferences(converter, synthesizedClasses, feedback);
+ rewriteLambdaReferences(converter, feedback);
this.strategyFactory = null;
}
@@ -304,10 +316,7 @@
}
}
- private void rewriteLambdaReferences(
- IRConverter converter,
- Map<DexType, DexProgramClass> synthesizedClasses,
- OptimizationFeedback feedback) {
+ private void rewriteLambdaReferences(IRConverter converter, OptimizationFeedback feedback) {
List<DexEncodedMethod> methods =
methodsToReprocess
.stream()
@@ -315,9 +324,9 @@
.collect(Collectors.toList());
for (DexEncodedMethod method : methods) {
DexEncodedMethod mappedMethod =
- converter.graphLense().mapDexEncodedMethod(method, converter.appInfo, synthesizedClasses);
+ converter.graphLense().mapDexEncodedMethod(method, converter.appInfo);
converter.processMethod(mappedMethod, feedback,
- x -> false, CallSiteInformation.empty(), Outliner::noProcessing);
+ Predicates.alwaysFalse(), CallSiteInformation.empty(), Outliner::noProcessing);
assert mappedMethod.isProcessed();
}
}
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 b32134a..8de2695 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -840,8 +840,8 @@
// If this type has deferred annotations, we have to process those now, too.
Set<DexAnnotation> annotations = deferredAnnotations.remove(type);
if (annotations != null && !annotations.isEmpty()) {
- assert !holder.accessFlags.isAnnotation()
- || annotations.stream().allMatch(a -> a.annotation.type == holder.type);
+ assert holder.accessFlags.isAnnotation();
+ assert annotations.stream().allMatch(a -> a.annotation.type == holder.type);
annotations.forEach(annotation -> handleAnnotation(holder, annotation));
}
} else {
@@ -1985,11 +1985,11 @@
/**
* Set of all fields which may be touched by a get operation. This is actual field definitions.
*/
- public final SortedSet<DexField> fieldsRead;
+ private final SortedSet<DexField> fieldsRead;
/**
* Set of all fields which may be touched by a put operation. This is actual field definitions.
*/
- public final SortedSet<DexField> fieldsWritten;
+ private final SortedSet<DexField> fieldsWritten;
/**
* Set of all field ids used in instance field reads, along with access context.
*/
@@ -2359,7 +2359,8 @@
public boolean isInstantiatedDirectly(DexType type) {
assert type.isClassType();
- return instantiatedTypes.contains(type)
+ return type.isD8R8SynthesizedClassType()
+ || instantiatedTypes.contains(type)
|| instantiatedLambdas.contains(type)
|| instantiatedAnnotationTypes.contains(type);
}
@@ -2386,6 +2387,31 @@
return isInstantiatedDirectly(type) || isInstantiatedIndirectly(type);
}
+ public boolean isFieldRead(DexField field) {
+ return fieldsRead.contains(field)
+ // TODO(b/121354886): Pinned fields should be in `fieldsRead`.
+ || isPinned(field)
+ // Fields in the class that is synthesized by D8/R8 would be used soon.
+ || field.getHolder().isD8R8SynthesizedClassType()
+ // For library classes we don't know whether a field is read.
+ || isLibraryField(field);
+ }
+
+ public boolean isFieldWritten(DexField field) {
+ return fieldsWritten.contains(field)
+ // TODO(b/121354886): Pinned fields should be in `fieldsWritten`.
+ || isPinned(field)
+ // Fields in the class that is synthesized by D8/R8 would be used soon.
+ || field.clazz.isD8R8SynthesizedClassType()
+ // For library classes we don't know whether a field is rewritten.
+ || isLibraryField(field);
+ }
+
+ private boolean isLibraryField(DexField field) {
+ DexClass holder = definitionFor(field.clazz);
+ return holder == null || holder.isLibraryClass();
+ }
+
private Object2BooleanMap<DexReference> joinIdentifierNameStrings(
Set<DexReference> explicit, Set<DexReference> implicit) {
Object2BooleanMap<DexReference> result = new Object2BooleanArrayMap<>();
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index dd8bcd6..786d026 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -228,8 +228,8 @@
Predicate<DexField> isReachableOrReferencedField =
field ->
appInfo.liveFields.contains(field)
- || appInfo.fieldsRead.contains(field)
- || appInfo.fieldsWritten.contains(field);
+ || appInfo.isFieldRead(field)
+ || appInfo.isFieldWritten(field);
int firstUnreachable =
firstUnreachableIndex(Arrays.asList(fields), isReachableOrReferencedField);
// Return the original array if all fields are used.
diff --git a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
index f8faf9c..e72cf0a 100644
--- a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
@@ -58,7 +58,7 @@
// the source file or the desguared interface is to make it an inner class.
assertEquals('$', InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX.charAt(0));
defaultMethodName = InterfaceMethodRewriter.DEFAULT_METHOD_PREFIX + "doSomething";
- defaultMethodThisName = "-this";
+ defaultMethodThisName = "_this";
}
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
index 3112ae7..32e8e4d 100644
--- a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
@@ -69,9 +70,13 @@
})
.run(Main.class)
.assertFailureWithErrorThatMatches(
- containsString(
- "java.lang.ClassNotFoundException: "
- + "Didn't find class \"com.android.tools.r8.naming.b124357885.Foo\""));
+ anyOf(
+ containsString(
+ "java.lang.ClassNotFoundException: "
+ + "Didn't find class \"com.android.tools.r8.naming.b124357885.Foo\""),
+ containsString(
+ "java.lang.NoClassDefFoundError: "
+ + "com/android/tools/r8/naming/b124357885/Foo")));
}
}
@@ -94,4 +99,4 @@
interface Foo<T> {}
-class FooImpl<T> implements Foo<T> {}
\ No newline at end of file
+class FooImpl<T> implements Foo<T> {}