Nest access control: d8 and r8 concurrent
1. d8 now processes program classes once, on-the-fly
instead of once ahead and once to rewrite.
This introduced concurrency issues since the
IRConverter processes methods concurrently, hence
the code had to be made thread-safe (Cf 2.)
2. nest processing in d8 and r8 is now thread-safe and
concurrent, i.e., each nest is processed concurrently
to others.
Bug:130529390
Change-Id: I4f3cd3e6f687e66218ad37db3c9b77299e51ca80
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 16b75f5..f48d075 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -426,8 +426,8 @@
if (options.enableNestBasedAccessDesugaring) {
timing.begin("NestBasedAccessDesugaring");
NestBasedAccessDesugaringAnalysis analyzer =
- new NestBasedAccessDesugaringAnalysis(appViewWithLiveness);
- boolean changed = appView.setGraphLense(analyzer.run());
+ new NestBasedAccessDesugaringAnalysis(appViewWithLiveness, executorService);
+ boolean changed = appView.setGraphLense(analyzer.run(executorService));
if (changed) {
appViewWithLiveness.setAppInfo(
appViewWithLiveness
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 a301563..bd99f55 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -832,6 +832,11 @@
return !nestMembers.isEmpty();
}
+ public DexType getNestHost() {
+ assert nestHost != null;
+ return nestHost.getNestHost();
+ }
+
public NestHostClassAttribute getNestHostClassAttribute() {
return nestHost;
}
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 c6c1ba8..c307286 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
@@ -290,15 +290,10 @@
return false;
}
- private void analyzeNests() {
+ private void desugarNestBasedAccess(Builder<?> builder, ExecutorService executorService)
+ throws ExecutionException {
if (nestBasedAccessDesugaringRewriter != null) {
- nestBasedAccessDesugaringRewriter.analyzeNests();
- }
- }
-
- private void synthetizeNestConstructor(Builder<?> builder) {
- if (nestBasedAccessDesugaringRewriter != null) {
- nestBasedAccessDesugaringRewriter.synthetizeNestConstructor(builder);
+ nestBasedAccessDesugaringRewriter.desugarNestBasedAccess(builder, executorService, this);
}
}
@@ -358,7 +353,6 @@
public DexApplication convertToDex(DexApplication application, ExecutorService executor)
throws ExecutionException {
removeLambdaDeserializationMethods();
- analyzeNests();
timing.begin("IR conversion");
convertClassesToDex(application.classes(), executor);
@@ -367,7 +361,7 @@
Builder<?> builder = application.builder();
builder.setHighestSortingString(highestSortingString);
- synthetizeNestConstructor(builder);
+ desugarNestBasedAccess(builder, executor);
synthesizeLambdaClasses(builder, executor);
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
synthesizeTwrCloseResourceUtilityClass(builder, executor);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 3f6695b..d48530c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -18,15 +18,19 @@
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+// NestBasedAccessDesugaring contains common code between the two subclasses
+// which are specialized for d8 and r8
public abstract class NestBasedAccessDesugaring {
// Short names to avoid creating long strings
@@ -44,51 +48,62 @@
private static final String FULL_NEST_CONTRUCTOR_NAME = "L" + NEST_CONSTRUCTOR_NAME + ";";
protected final AppView<?> appView;
- private final HashMap<DexEncodedMethod, DexEncodedMethod> bridges = new HashMap<>();
- private final HashMap<DexFieldWithAccess, DexEncodedMethod> fieldBridges = new HashMap<>();
- private final HashMap<DexEncodedMethod, DexProgramClass> deferredBridgesToAdd = new HashMap<>();
+ // Following maps are there to avoid creating the bridges multiple times.
+ private final Map<DexEncodedMethod, DexEncodedMethod> bridges = new ConcurrentHashMap<>();
+ private final Map<DexEncodedField, DexEncodedMethod> getFieldBridges = new ConcurrentHashMap<>();
+ private final Map<DexEncodedField, DexEncodedMethod> putFieldBridges = new ConcurrentHashMap<>();
+ // The following map records the bridges to add in the program.
+ // It may differ from the values of the previous maps
+ // if come classes are on the classpath and not the program path.
+ final Map<DexEncodedMethod, DexProgramClass> deferredBridgesToAdd = new ConcurrentHashMap<>();
+ // Common single empty class for nest based private constructors
private DexProgramClass nestConstructor;
- public NestBasedAccessDesugaring(AppView<?> appView) {
+ NestBasedAccessDesugaring(AppView<?> appView) {
this.appView = appView;
}
- protected DexType getConstructorType() {
- if (nestConstructor == null) {
- return null;
+ DexType getNestConstructorType() {
+ return nestConstructor == null ? null : nestConstructor.type;
+ }
+
+ // Extract the list of types in the programClass' nest, of host hostClass
+ List<DexType> extractNest(DexClass hostClass, DexProgramClass programClass) {
+ assert programClass != null;
+ if (hostClass == null) {
+ throw abortCompilationDueToMissingNestHost(programClass);
}
- return nestConstructor.type;
- }
-
- public void analyzeNests() {
- // TODO(b/130529338) we don't need to compute a list with all live nests.
- // we just need to iterate all live nests.
- List<List<DexType>> liveNests = computeLiveNests();
- processLiveNests(liveNests);
- addDeferredBridges();
- }
-
- public void synthetizeNestConstructor(DexApplication.Builder<?> builder) {
- if (nestConstructor != null) {
- appView.appInfo().addSynthesizedClass(nestConstructor);
- builder.addSynthesizedClass(nestConstructor, true);
+ List<DexType> classesInNest =
+ new ArrayList<>(hostClass.getNestMembersClassAttributes().size() + 1);
+ for (NestMemberClassAttribute nestmate : hostClass.getNestMembersClassAttributes()) {
+ classesInNest.add(nestmate.getNestMember());
}
+ classesInNest.add(hostClass.type);
+ return classesInNest;
}
- private void addDeferredBridges() {
- for (Map.Entry<DexEncodedMethod, DexProgramClass> entry : deferredBridgesToAdd.entrySet()) {
- entry.getValue().addMethod(entry.getKey());
- }
- }
-
- private void processLiveNests(List<List<DexType>> liveNests) {
+ void processNestsConcurrently(List<List<DexType>> liveNests, ExecutorService executorService)
+ throws ExecutionException {
+ List<Future<?>> futures = new ArrayList<>();
for (List<DexType> nest : liveNests) {
- for (DexType type : nest) {
- DexClass clazz = appView.definitionFor(type);
- if (clazz == null) {
- // TODO(b/130529338) We could throw only a warning if a class is missing.
- throw abortCompilationDueToIncompleteNest(nest);
- }
+ futures.add(
+ executorService.submit(
+ () -> {
+ processNest(nest);
+ return null; // we want a Callable not a Runnable to be able to throw
+ }));
+ }
+ ThreadUtils.awaitFutures(futures);
+ }
+
+ private void processNest(List<DexType> nest) {
+ for (DexType type : nest) {
+ DexClass clazz = appView.definitionFor(type);
+ if (clazz == null) {
+ // TODO(b/130529338) We could throw only a warning if a class is missing.
+ throw abortCompilationDueToIncompleteNest(nest);
+ }
+ if (shouldProcessClassInNest(clazz, nest)) {
NestBasedAccessDesugaringUseRegistry registry =
new NestBasedAccessDesugaringUseRegistry(nest, clazz);
for (DexEncodedMethod method : clazz.methods()) {
@@ -98,6 +113,14 @@
}
}
+ protected abstract boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest);
+
+ void addDeferredBridges() {
+ for (Map.Entry<DexEncodedMethod, DexProgramClass> entry : deferredBridgesToAdd.entrySet()) {
+ entry.getValue().addMethod(entry.getKey());
+ }
+ }
+
private RuntimeException abortCompilationDueToIncompleteNest(List<DexType> nest) {
List<String> programClassesFromNest = new ArrayList<>();
List<String> unavailableClasses = new ArrayList<>();
@@ -128,38 +151,6 @@
throw new CompilationError(stringBuilder.toString());
}
- private List<List<DexType>> computeLiveNests() {
- List<List<DexType>> liveNests = new ArrayList<>();
- // It is possible that a nest member is on the program path but its nest host
- // is only in the class path. Nests are therefore computed the first time a
- // nest member is met, host or not. The computedNestHosts list is there to
- // avoid processing multiple times the same nest.
- Set<DexType> computedNestHosts = new HashSet<>();
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (clazz.isInANest()) {
- DexType hostType =
- clazz.isNestHost() ? clazz.type : clazz.getNestHostClassAttribute().getNestHost();
- if (!computedNestHosts.contains(hostType)) {
- computedNestHosts.add(hostType);
- DexClass host =
- clazz.isNestHost()
- ? clazz
- : appView.definitionFor(clazz.getNestHostClassAttribute().getNestHost());
- if (host == null) {
- throw abortCompilationDueToMissingNestHost(clazz);
- }
- List<DexType> classesInNest = new ArrayList<>();
- for (NestMemberClassAttribute nestmate : host.getNestMembersClassAttributes()) {
- classesInNest.add(nestmate.getNestMember());
- }
- classesInNest.add(host.type);
- liveNests.add(classesInNest);
- }
- }
- }
- return liveNests;
- }
-
private RuntimeException abortCompilationDueToMissingNestHost(DexProgramClass compiledClass) {
String nestHostName = compiledClass.getNestHostClassAttribute().getNestHost().getName();
throw new CompilationError(
@@ -170,6 +161,16 @@
+ " to be on program or class path for compilation to succeed.");
}
+ private RuntimeException abortCompilationDueToBridgeRequiredOnLibraryClass(
+ DexClass compiledClass, DexClass libraryClass) {
+ throw new CompilationError(
+ "Class "
+ + compiledClass.type.getName()
+ + " requires the insertion of a bridge on the library class "
+ + libraryClass.type.getName()
+ + " which is impossible.");
+ }
+
protected abstract void shouldRewriteCalls(DexMethod method, DexMethod bridge);
protected abstract void shouldRewriteInitializers(DexMethod method, DexMethod bridge);
@@ -182,16 +183,6 @@
protected abstract void shouldRewriteInstancePutFields(DexField field, DexMethod bridge);
- private RuntimeException abortCompilationDueToBridgeRequiredOnLibraryClass(
- DexClass compiledClass, DexClass libraryClass) {
- throw new CompilationError(
- "Class "
- + compiledClass.type.getName()
- + " requires the insertion of a bridge on the library class "
- + libraryClass.type.getName()
- + " which is impossible.");
- }
-
private DexString methodBridgeName(DexEncodedMethod method) {
String methodName = method.method.name.toString();
String fullName;
@@ -247,7 +238,97 @@
return nestConstructor;
}
- private class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
+ void synthetizeNestConstructor(DexApplication.Builder<?> builder) {
+ if (nestConstructor != null) {
+ appView.appInfo().addSynthesizedClass(nestConstructor);
+ builder.addSynthesizedClass(nestConstructor, true);
+ }
+ }
+
+ boolean registerFieldAccess(
+ DexField field, boolean isGet, List<DexType> nest, DexClass currentClass) {
+ if (field.holder == currentClass.type || !nest.contains(field.holder)) {
+ return false;
+ }
+ DexEncodedField target = appView.definitionFor(field);
+ if (target == null || !target.accessFlags.isPrivate()) {
+ return false;
+ }
+ Map<DexEncodedField, DexEncodedMethod> fieldMap = isGet ? getFieldBridges : putFieldBridges;
+ DexEncodedMethod bridge =
+ fieldMap.computeIfAbsent(
+ target,
+ k -> {
+ DexFieldWithAccess fieldWithAccess = new DexFieldWithAccess(target, isGet);
+ DexClass holder = appView.definitionFor(field.holder);
+ DexEncodedMethod localBridge =
+ DexEncodedMethod.createFieldAccessorBridge(
+ fieldWithAccess, holder, appView, fieldBridgeName(fieldWithAccess));
+ // Accesses to program classes private members require bridge insertion.
+ if (holder.isProgramClass()) {
+ deferredBridgesToAdd.put(localBridge, holder.asProgramClass());
+ } else if (holder.isLibraryClass()) {
+ throw abortCompilationDueToBridgeRequiredOnLibraryClass(currentClass, holder);
+ }
+ return localBridge;
+ });
+ // In program classes, any access to nest mate private member needs to be rewritten.
+ if (currentClass.isProgramClass()) {
+ if (isGet) {
+ if (target.isStatic()) {
+ shouldRewriteStaticGetFields(field, bridge.method);
+ } else {
+ shouldRewriteInstanceGetFields(field, bridge.method);
+ }
+ } else {
+ if (target.isStatic()) {
+ shouldRewriteStaticPutFields(field, bridge.method);
+ } else {
+ shouldRewriteInstancePutFields(field, bridge.method);
+ }
+ }
+ }
+ return true;
+ }
+
+ boolean registerInvoke(DexMethod method, List<DexType> nest, DexClass currentClass) {
+ if (method.holder == currentClass.type || !nest.contains(method.holder)) {
+ return false;
+ }
+ DexEncodedMethod target = appView.definitionFor(method);
+ if (target == null || !target.accessFlags.isPrivate()) {
+ return false;
+ }
+ DexEncodedMethod bridge =
+ bridges.computeIfAbsent(
+ target,
+ k -> {
+ DexClass holder = appView.definitionFor(method.holder);
+ DexEncodedMethod localBridge =
+ target.isInstanceInitializer()
+ ? target.toInitializerForwardingBridge(
+ holder, appView, ensureNestConstructorClass())
+ : target.toStaticForwardingBridge(holder, appView, methodBridgeName(target));
+ // Accesses to program classes private members require bridge insertion.
+ if (holder.isProgramClass()) {
+ deferredBridgesToAdd.put(localBridge, holder.asProgramClass());
+ } else if (holder.isLibraryClass()) {
+ throw abortCompilationDueToBridgeRequiredOnLibraryClass(currentClass, holder);
+ }
+ return localBridge;
+ });
+ // In program classes, any access to nest mate private member needs to be rewritten.
+ if (currentClass.isProgramClass()) {
+ if (target.isInstanceInitializer()) {
+ shouldRewriteInitializers(method, bridge.method);
+ } else {
+ shouldRewriteCalls(method, bridge.method);
+ }
+ }
+ return true;
+ }
+
+ protected class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
private final List<DexType> nest;
private final DexClass currentClass;
@@ -258,110 +339,28 @@
this.currentClass = currentClass;
}
- private boolean registerInvoke(DexMethod method) {
- if (method.holder == currentClass.type || !nest.contains(method.holder)) {
- return false;
- }
- DexEncodedMethod target = appView.definitionFor(method);
- if (target == null || !target.accessFlags.isPrivate()) {
- return false;
- }
- DexEncodedMethod bridge =
- bridges.computeIfAbsent(
- target,
- k -> {
- DexClass holder = appView.definitionFor(method.holder);
- DexEncodedMethod localBridge =
- target.isInstanceInitializer()
- ? target.toInitializerForwardingBridge(
- holder, appView, ensureNestConstructorClass())
- : target.toStaticForwardingBridge(
- holder, appView, methodBridgeName(target));
- // Accesses to program classes private members require bridge insertion.
- if (holder.isProgramClass()) {
- deferredBridgesToAdd.put(localBridge, holder.asProgramClass());
- } else if (holder.isLibraryClass()) {
- throw abortCompilationDueToBridgeRequiredOnLibraryClass(currentClass, holder);
- }
- return localBridge;
- });
- // In program classes, any access to nest mate private member needs to be rewritten.
- if (currentClass.isProgramClass()) {
- if (target.isInstanceInitializer()) {
- shouldRewriteInitializers(method, bridge.method);
- } else {
- shouldRewriteCalls(method, bridge.method);
- }
- }
- return true;
- }
-
- private boolean registerFieldAccess(DexField field, boolean isGet) {
- if (field.holder == currentClass.type || !nest.contains(field.holder)) {
- return false;
- }
- DexEncodedField target = appView.definitionFor(field);
- if (target == null || !target.accessFlags.isPrivate()) {
- return false;
- }
- DexFieldWithAccess key = new DexFieldWithAccess(target, isGet);
- DexEncodedMethod bridge =
- fieldBridges.computeIfAbsent(
- key,
- k -> {
- DexClass holder = appView.definitionFor(field.holder);
- DexEncodedMethod localBridge =
- DexEncodedMethod.createFieldAccessorBridge(
- key, holder, appView, fieldBridgeName(key));
- // Accesses to program classes private members require bridge insertion.
- if (holder.isProgramClass()) {
- deferredBridgesToAdd.put(localBridge, holder.asProgramClass());
- } else if (holder.isLibraryClass()) {
- throw abortCompilationDueToBridgeRequiredOnLibraryClass(currentClass, holder);
- }
- return localBridge;
- });
- // In program classes, any access to nest mate private member needs to be rewritten.
- if (currentClass.isProgramClass()) {
- if (isGet) {
- if (target.isStatic()) {
- shouldRewriteStaticGetFields(field, bridge.method);
- } else {
- shouldRewriteInstanceGetFields(field, bridge.method);
- }
- } else {
- if (target.isStatic()) {
- shouldRewriteStaticPutFields(field, bridge.method);
- } else {
- shouldRewriteInstancePutFields(field, bridge.method);
- }
- }
- }
- return true;
- }
-
@Override
public boolean registerInvokeVirtual(DexMethod method) {
// Calls to class nest mate private methods are targeted by invokeVirtual in jdk11.
// The spec recommends to do so, but do not enforce it, hence invokeDirect is also registered.
- return registerInvoke(method);
+ return registerInvoke(method, nest, currentClass);
}
@Override
public boolean registerInvokeDirect(DexMethod method) {
- return registerInvoke(method);
+ return registerInvoke(method, nest, currentClass);
}
@Override
public boolean registerInvokeStatic(DexMethod method) {
- return registerInvoke(method);
+ return registerInvoke(method, nest, currentClass);
}
@Override
public boolean registerInvokeInterface(DexMethod method) {
// Calls to interface nest mate private methods are targeted by invokeInterface in jdk11.
// The spec recommends to do so, but do not enforce it, hence invokeDirect is also registered.
- return registerInvoke(method);
+ return registerInvoke(method, nest, currentClass);
}
@Override
@@ -372,12 +371,12 @@
@Override
public boolean registerInstanceFieldWrite(DexField field) {
- return registerFieldAccess(field, false);
+ return registerFieldAccess(field, false, nest, currentClass);
}
@Override
public boolean registerInstanceFieldRead(DexField field) {
- return registerFieldAccess(field, true);
+ return registerFieldAccess(field, true, nest, currentClass);
}
@Override
@@ -390,12 +389,12 @@
@Override
public boolean registerStaticFieldRead(DexField field) {
- return registerFieldAccess(field, true);
+ return registerFieldAccess(field, true, nest, currentClass);
}
@Override
public boolean registerStaticFieldWrite(DexField field) {
- return registerFieldAccess(field, false);
+ return registerFieldAccess(field, false, nest, currentClass);
}
@Override
@@ -410,7 +409,7 @@
private final DexEncodedField field;
private final boolean isGet;
- public DexFieldWithAccess(DexEncodedField field, boolean isGet) {
+ DexFieldWithAccess(DexEncodedField field, boolean isGet) {
this.field = field;
this.isGet = isGet;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringAnalysis.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringAnalysis.java
index de92422..e341776 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringAnalysis.java
@@ -1,54 +1,96 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexField;
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.GraphLense;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+// R8 specific desugaring of nest based access.
+// Summary:
+// - Computes all the live nests reachable from Program Classes (Sequential).
+// - Process all live nests finding nest based access (Nests processes concurrently).
+// - Add bridges to be processed by further passes (Sequential).
public class NestBasedAccessDesugaringAnalysis extends NestBasedAccessDesugaring {
private final NestedPrivateMethodLense.Builder builder;
- public NestBasedAccessDesugaringAnalysis(AppView<?> appView) {
+ public NestBasedAccessDesugaringAnalysis(AppView<?> appView, ExecutorService executorService) {
super(appView);
this.builder = NestedPrivateMethodLense.builder(appView);
}
- public GraphLense run() {
+ public GraphLense run(ExecutorService executorService) throws ExecutionException {
if (appView.options().canUseNestBasedAccess()) {
return appView.graphLense();
}
- analyzeNests();
- return builder.build(appView.graphLense(), getConstructorType());
+ List<List<DexType>> liveNests = computeLiveNests();
+ processNestsConcurrently(liveNests, executorService);
+ addDeferredBridges();
+ return builder.build(appView.graphLense(), getNestConstructorType());
+ }
+
+ private List<List<DexType>> computeLiveNests() {
+ List<List<DexType>> liveNests = new ArrayList<>();
+ // It is possible that a nest member is on the program path but its nest host
+ // is only in the class path. Nests are therefore computed the first time a
+ // nest member is met, host or not. The computedNestHosts list is there to
+ // avoid processing multiple times the same nest.
+ Set<DexType> computedNestHosts = new HashSet<>();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.isInANest()) {
+ DexType hostType = clazz.isNestHost() ? clazz.type : clazz.getNestHost();
+ if (!computedNestHosts.contains(hostType)) {
+ computedNestHosts.add(hostType);
+ DexClass host = clazz.isNestHost() ? clazz : appView.definitionFor(clazz.getNestHost());
+ liveNests.add(extractNest(host, clazz));
+ }
+ }
+ }
+ return liveNests;
+ }
+
+ // In R8, all classes are processed ahead of time.
+ @Override
+ protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) {
+ return true;
}
@Override
- protected void shouldRewriteInitializers(DexMethod method, DexMethod bridge) {
+ protected synchronized void shouldRewriteInitializers(DexMethod method, DexMethod bridge) {
builder.mapInitializer(method, bridge);
}
@Override
- protected void shouldRewriteCalls(DexMethod method, DexMethod bridge) {
+ protected synchronized void shouldRewriteCalls(DexMethod method, DexMethod bridge) {
builder.map(method, bridge);
}
@Override
- protected void shouldRewriteStaticGetFields(DexField field, DexMethod bridge) {
+ protected synchronized void shouldRewriteStaticGetFields(DexField field, DexMethod bridge) {
builder.mapStaticGet(field, bridge);
}
@Override
- protected void shouldRewriteStaticPutFields(DexField field, DexMethod bridge) {
+ protected synchronized void shouldRewriteStaticPutFields(DexField field, DexMethod bridge) {
builder.mapStaticPut(field, bridge);
}
@Override
- protected void shouldRewriteInstanceGetFields(DexField field, DexMethod bridge) {
+ protected synchronized void shouldRewriteInstanceGetFields(DexField field, DexMethod bridge) {
builder.mapInstanceGet(field, bridge);
}
@Override
- protected void shouldRewriteInstancePutFields(DexField field, DexMethod bridge) {
+ protected synchronized void shouldRewriteInstancePutFields(DexField field, DexMethod bridge) {
builder.mapInstancePut(field, bridge);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java
index ed4cfbe..d4e25df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java
@@ -1,10 +1,15 @@
package com.android.tools.r8.ir.desugar;
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.DexField;
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.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -12,20 +17,33 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRConverter;
import java.util.ArrayList;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+// D8 specific nest based access desugaring.
+// Summary:
+// - Process all methods compiled rewriting nest based access (Methods processed concurrently).
+// - Process classes on class path in reachable nests to find bridges to add
+// in Program classes (Nests processed concurrently).
+// - Add bridges and nest constructor class (Sequential).
+// - Optimize bridges (Bridges processed concurrently).
public class NestBasedAccessDesugaringRewriter extends NestBasedAccessDesugaring {
- private Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
- private Map<DexMethod, DexMethod> initializerMap = new IdentityHashMap<>();
- private final Map<DexField, DexMethod> staticGetToMethodMap = new IdentityHashMap<>();
- private final Map<DexField, DexMethod> staticPutToMethodMap = new IdentityHashMap<>();
- private final Map<DexField, DexMethod> instanceGetToMethodMap = new IdentityHashMap<>();
- private final Map<DexField, DexMethod> instancePutToMethodMap = new IdentityHashMap<>();
+ private final Map<DexMethod, DexMethod> methodMap = new ConcurrentHashMap<>();
+ private final Map<DexMethod, DexMethod> initializerMap = new ConcurrentHashMap<>();
+ private final Map<DexField, DexMethod> staticGetToMethodMap = new ConcurrentHashMap<>();
+ private final Map<DexField, DexMethod> staticPutToMethodMap = new ConcurrentHashMap<>();
+ private final Map<DexField, DexMethod> instanceGetToMethodMap = new ConcurrentHashMap<>();
+ private final Map<DexField, DexMethod> instancePutToMethodMap = new ConcurrentHashMap<>();
+
+ // Map the nest host to its nest members, including the nest host itself.
+ private final Map<DexType, List<DexType>> metNests = new ConcurrentHashMap<>();
public NestBasedAccessDesugaringRewriter(AppView<?> appView) {
super(appView);
@@ -61,21 +79,37 @@
instancePutToMethodMap.put(field, bridge);
}
+ private List<DexType> getNestFor(DexProgramClass programClass) {
+ if (!programClass.isInANest()) {
+ return null;
+ }
+ DexType nestHost = programClass.isNestHost() ? programClass.type : programClass.getNestHost();
+ return metNests.computeIfAbsent(
+ nestHost, host -> extractNest(appView.definitionFor(nestHost), programClass));
+ }
+
private void rewriteFieldAccess(
- Instruction instruction,
+ FieldInstruction fieldInstruction,
InstructionListIterator instructions,
DexMethod method,
Map<DexField, DexMethod> fieldToMethodMap) {
- DexField field = instruction.asFieldInstruction().getField();
- DexMethod newTarget = fieldToMethodMap.get(field);
+ DexMethod newTarget = fieldToMethodMap.get(fieldInstruction.getField());
if (newTarget != null && method != newTarget) {
instructions.replaceCurrentInstruction(
- new InvokeStatic(newTarget, instruction.outValue(), instruction.inValues()));
+ new InvokeStatic(newTarget, fieldInstruction.outValue(), fieldInstruction.inValues()));
}
}
public void rewriteNestBasedAccesses(
DexEncodedMethod encodedMethod, IRCode code, AppView<?> appView) {
+ // We are compiling its code so it has to be a non-null program class.
+ DexProgramClass currentClass =
+ appView.definitionFor(encodedMethod.method.holder).asProgramClass();
+ List<DexType> nest = getNestFor(currentClass);
+ if (nest == null) {
+ return;
+ }
+
ListIterator<BasicBlock> blocks = code.listIterator();
while (blocks.hasNext()) {
BasicBlock block = blocks.next();
@@ -85,6 +119,7 @@
if (instruction.isInvokeMethod() && !instruction.isInvokeSuper()) {
InvokeMethod invokeMethod = instruction.asInvokeMethod();
DexMethod methodCalled = invokeMethod.getInvokedMethod();
+ registerInvoke(methodCalled, nest, currentClass);
DexMethod newTarget = methodMap.get(methodCalled);
if (newTarget != null && encodedMethod.method != newTarget) {
instructions.replaceCurrentInstruction(
@@ -103,20 +138,44 @@
new InvokeDirect(newTarget, invokeMethod.outValue(), parameters));
}
}
- } else if (instruction.isInstanceGet()) {
- rewriteFieldAccess(
- instruction, instructions, encodedMethod.method, instanceGetToMethodMap);
- } else if (instruction.isInstancePut()) {
- rewriteFieldAccess(
- instruction, instructions, encodedMethod.method, instancePutToMethodMap);
- } else if (instruction.isStaticGet()) {
- rewriteFieldAccess(instruction, instructions, encodedMethod.method, staticGetToMethodMap);
- } else if (instruction.isStaticPut()) {
- rewriteFieldAccess(instruction, instructions, encodedMethod.method, staticPutToMethodMap);
+ } else if (instruction.isFieldInstruction()) {
+ FieldInstruction fieldInstruction = instruction.asFieldInstruction();
+ if (instruction.isInstanceGet()) {
+ registerFieldAccess(fieldInstruction.getField(), true, nest, currentClass);
+ rewriteFieldAccess(
+ fieldInstruction, instructions, encodedMethod.method, instanceGetToMethodMap);
+ } else if (instruction.isInstancePut()) {
+ registerFieldAccess(fieldInstruction.getField(), false, nest, currentClass);
+ rewriteFieldAccess(
+ fieldInstruction, instructions, encodedMethod.method, instancePutToMethodMap);
+ } else if (instruction.isStaticGet()) {
+ registerFieldAccess(fieldInstruction.getField(), true, nest, currentClass);
+ rewriteFieldAccess(
+ fieldInstruction, instructions, encodedMethod.method, staticGetToMethodMap);
+ } else if (instruction.isStaticPut()) {
+ registerFieldAccess(fieldInstruction.getField(), false, nest, currentClass);
+ rewriteFieldAccess(
+ fieldInstruction, instructions, encodedMethod.method, staticPutToMethodMap);
+ }
}
}
}
}
+ public void desugarNestBasedAccess(
+ DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
+ throws ExecutionException {
+ List<List<DexType>> metNests = new ArrayList<>(this.metNests.values());
+ processNestsConcurrently(metNests, executorService);
+ addDeferredBridges();
+ synthetizeNestConstructor(builder);
+ converter.optimizeSynthesizedMethodsConcurrently(
+ deferredBridgesToAdd.keySet(), executorService);
+ }
+ // In D8, programClass are processed on the fly so they do not need to be processed again here.
+ @Override
+ protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) {
+ return clazz.isNotProgramClass();
+ }
}