Revert R8 Nest based desugaring to use map
- We cannot use definitionFor in the Lens
since the program has changed.
- We need to keep maps to correctly remap fields/
methods.
- Fix bug where lookupMethod(DexMethod) was called in cases
where the lens+method is context sensitive
Bug:132770201
Change-Id: I8207e8bea3fe3870ce9248276fa9cdada444c385
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
index c79e13a..2530225 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -62,7 +63,7 @@
DexMethod methodCalled = invokeMethod.getInvokedMethod();
DexEncodedMethod encodedMethodCalled = appView.definitionFor(methodCalled);
if (encodedMethodCalled != null
- && invokeRequiresRewriting(encodedMethodCalled, currentClass, appView)) {
+ && invokeRequiresRewriting(encodedMethodCalled, currentClass)) {
DexMethod bridge = ensureInvokeBridge(encodedMethodCalled);
if (encodedMethodCalled.isInstanceInitializer()) {
instructions.previous();
@@ -82,8 +83,7 @@
} else if (instruction.isFieldInstruction()) {
DexEncodedField encodedField =
appView.definitionFor(instruction.asFieldInstruction().getField());
- if (encodedField != null
- && fieldAccessRequiresRewriting(encodedField, currentClass, appView)) {
+ if (encodedField != null && fieldAccessRequiresRewriting(encodedField, currentClass)) {
if (instruction.isInstanceGet() || instruction.isStaticGet()) {
DexMethod bridge = ensureFieldAccessBridge(encodedField, true);
instructions.replaceCurrentInstruction(
@@ -108,6 +108,32 @@
ThreadUtils.awaitFutures(futures);
}
+ private void addDeferredBridges() {
+ addDeferredBridges(bridges.values());
+ addDeferredBridges(getFieldBridges.values());
+ addDeferredBridges(putFieldBridges.values());
+ }
+
+ private void addDeferredBridges(Collection<DexEncodedMethod> bridges) {
+ for (DexEncodedMethod bridge : bridges) {
+ DexClass holder = definitionFor(bridge.method.holder);
+ assert holder != null && holder.isProgramClass();
+ holder.asProgramClass().addMethod(bridge);
+ }
+ }
+
+ private void optimizeDeferredBridgesConcurrently(
+ ExecutorService executorService, IRConverter converter) throws ExecutionException {
+ List<Future<?>> futures =
+ new ArrayList<>(bridges.size() + getFieldBridges.size() + putFieldBridges.size());
+ converter.optimizeSynthesizedMethodsConcurrently(bridges.values(), executorService, futures);
+ converter.optimizeSynthesizedMethodsConcurrently(
+ getFieldBridges.values(), executorService, futures);
+ converter.optimizeSynthesizedMethodsConcurrently(
+ putFieldBridges.values(), executorService, futures);
+ ThreadUtils.awaitFutures(futures);
+ }
+
public void desugarNestBasedAccess(
DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
throws ExecutionException {
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 8086767..4b9224c 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
@@ -19,19 +19,16 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringDiagnostic;
-import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -56,9 +53,9 @@
protected final AppView<?> appView;
// Following maps are there to avoid creating the bridges multiple times
// and remember the bridges to add once the nests are processed.
- private final Map<DexEncodedMethod, DexEncodedMethod> bridges = new ConcurrentHashMap<>();
- private final Map<DexEncodedField, DexEncodedMethod> getFieldBridges = new ConcurrentHashMap<>();
- private final Map<DexEncodedField, DexEncodedMethod> putFieldBridges = new ConcurrentHashMap<>();
+ final Map<DexMethod, DexEncodedMethod> bridges = new ConcurrentHashMap<>();
+ final Map<DexField, DexEncodedMethod> getFieldBridges = new ConcurrentHashMap<>();
+ final Map<DexField, DexEncodedMethod> putFieldBridges = new ConcurrentHashMap<>();
// Common single empty class for nest based private constructors
private final DexProgramClass nestConstructor;
private boolean nestConstructorUsed = false;
@@ -72,10 +69,24 @@
return nestConstructor.type;
}
+ protected DexClass definitionFor(DexType type) {
+ return appView.definitionFor(appView.graphLense().lookupType(type));
+ }
+
+ private DexEncodedMethod definitionFor(
+ DexMethod method, DexMethod context, Invoke.Type invokeType) {
+ return appView.definitionFor(
+ appView.graphLense().lookupMethod(method, context, invokeType).getMethod());
+ }
+
+ private DexEncodedField definitionFor(DexField field) {
+ return appView.definitionFor(appView.graphLense().lookupField(field));
+ }
+
// Extract the list of types in the programClass' nest, of host hostClass
private List<DexType> extractNest(DexClass clazz) {
assert clazz != null;
- DexClass hostClass = clazz.isNestHost() ? clazz : appView.definitionFor(clazz.getNestHost());
+ DexClass hostClass = clazz.isNestHost() ? clazz : definitionFor(clazz.getNestHost());
if (hostClass == null) {
reportMissingNestHost(clazz);
return null;
@@ -93,6 +104,7 @@
return executorService.submit(
() -> {
List<DexType> nest = extractNest(clazz);
+ // Nest is null when nest host is missing, we do nothing in this case.
if (nest != null) {
processNest(nest);
}
@@ -103,7 +115,7 @@
private void processNest(List<DexType> nest) {
boolean reported = false;
for (DexType type : nest) {
- DexClass clazz = appView.definitionFor(type);
+ DexClass clazz = definitionFor(type);
if (clazz == null) {
if (!reported) {
reportIncompleteNest(nest);
@@ -114,6 +126,7 @@
NestBasedAccessDesugaringUseRegistry registry =
new NestBasedAccessDesugaringUseRegistry(clazz);
for (DexEncodedMethod method : clazz.methods()) {
+ registry.setContext(method.method);
method.registerCodeReferences(registry);
}
}
@@ -123,39 +136,13 @@
protected abstract boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest);
- void addDeferredBridges() {
- addDeferredBridges(bridges.values());
- addDeferredBridges(getFieldBridges.values());
- addDeferredBridges(putFieldBridges.values());
- }
-
- private void addDeferredBridges(Collection<DexEncodedMethod> bridges) {
- for (DexEncodedMethod bridge : bridges) {
- DexClass holder = appView.definitionFor(bridge.method.holder);
- assert holder != null && holder.isProgramClass();
- holder.asProgramClass().addMethod(bridge);
- }
- }
-
- void optimizeDeferredBridgesConcurrently(ExecutorService executorService, IRConverter converter)
- throws ExecutionException {
- List<Future<?>> futures =
- new ArrayList<>(bridges.size() + getFieldBridges.size() + putFieldBridges.size());
- converter.optimizeSynthesizedMethodsConcurrently(bridges.values(), executorService, futures);
- converter.optimizeSynthesizedMethodsConcurrently(
- getFieldBridges.values(), executorService, futures);
- converter.optimizeSynthesizedMethodsConcurrently(
- putFieldBridges.values(), executorService, futures);
- ThreadUtils.awaitFutures(futures);
- }
-
private void reportIncompleteNest(List<DexType> nest) {
List<String> programClassesFromNest = new ArrayList<>();
List<String> unavailableClasses = new ArrayList<>();
List<String> classPathClasses = new ArrayList<>();
List<String> libraryClasses = new ArrayList<>();
for (DexType type : nest) {
- DexClass clazz = appView.definitionFor(type);
+ DexClass clazz = definitionFor(type);
if (clazz == null) {
unavailableClasses.add(type.getName());
} else if (clazz.isLibraryClass()) {
@@ -240,7 +227,7 @@
return type.getName().equals(NEST_CONSTRUCTOR_NAME);
}
- private static DexString computeMethodBridgeName(DexEncodedMethod method, AppView<?> appView) {
+ private DexString computeMethodBridgeName(DexEncodedMethod method) {
String methodName = method.method.name.toString();
String fullName;
if (method.isStatic()) {
@@ -251,8 +238,7 @@
return appView.dexItemFactory().createString(fullName);
}
- private static DexString computeFieldBridgeName(
- DexEncodedField field, boolean isGet, AppView<?> appView) {
+ private DexString computeFieldBridgeName(DexEncodedField field, boolean isGet) {
String fieldName = field.field.name.toString();
String fullName;
if (isGet && !field.isStatic()) {
@@ -267,7 +253,7 @@
return appView.dexItemFactory().createString(fullName);
}
- static DexMethod computeMethodBridge(DexEncodedMethod encodedMethod, AppView<?> appView) {
+ private DexMethod computeMethodBridge(DexEncodedMethod encodedMethod) {
DexMethod method = encodedMethod.method;
DexProto proto =
encodedMethod.accessFlags.isStatic()
@@ -275,17 +261,16 @@
: appView.dexItemFactory().prependTypeToProto(method.holder, method.proto);
return appView
.dexItemFactory()
- .createMethod(method.holder, proto, computeMethodBridgeName(encodedMethod, appView));
+ .createMethod(method.holder, proto, computeMethodBridgeName(encodedMethod));
}
- static DexMethod computeInitializerBridge(
- DexMethod method, AppView<?> appView, DexType nestConstructorType) {
+ private DexMethod computeInitializerBridge(DexMethod method) {
DexProto newProto =
- appView.dexItemFactory().appendTypeToProto(method.proto, nestConstructorType);
+ appView.dexItemFactory().appendTypeToProto(method.proto, nestConstructor.type);
return appView.dexItemFactory().createMethod(method.holder, newProto, method.name);
}
- static DexMethod computeFieldBridge(DexEncodedField field, boolean isGet, AppView<?> appView) {
+ private DexMethod computeFieldBridge(DexEncodedField field, boolean isGet) {
DexType holderType = field.field.holder;
DexType fieldType = field.field.type;
int bridgeParameterCount =
@@ -301,29 +286,27 @@
DexProto proto = appView.dexItemFactory().createProto(returnType, parameters);
return appView
.dexItemFactory()
- .createMethod(holderType, proto, computeFieldBridgeName(field, isGet, appView));
+ .createMethod(holderType, proto, computeFieldBridgeName(field, isGet));
}
- static boolean invokeRequiresRewriting(
- DexEncodedMethod method, DexClass contextClass, AppView<?> appView) {
+ boolean invokeRequiresRewriting(DexEncodedMethod method, DexClass contextClass) {
assert method != null;
// Rewrite only when targeting other nest members private fields.
if (!method.accessFlags.isPrivate() || method.method.holder == contextClass.type) {
return false;
}
- DexClass methodHolder = appView.definitionFor(method.method.holder);
+ DexClass methodHolder = definitionFor(method.method.holder);
assert methodHolder != null; // from encodedMethod
return methodHolder.getNestHost() == contextClass.getNestHost();
}
- static boolean fieldAccessRequiresRewriting(
- DexEncodedField field, DexClass contextClass, AppView<?> appView) {
+ boolean fieldAccessRequiresRewriting(DexEncodedField field, DexClass contextClass) {
assert field != null;
// Rewrite only when targeting other nest members private fields.
if (!field.accessFlags.isPrivate() || field.field.holder == contextClass.type) {
return false;
}
- DexClass fieldHolder = appView.definitionFor(field.field.holder);
+ DexClass fieldHolder = definitionFor(field.field.holder);
assert fieldHolder != null; // from encodedField
return fieldHolder.getNestHost() == contextClass.getNestHost();
}
@@ -345,17 +328,17 @@
}
DexMethod ensureFieldAccessBridge(DexEncodedField field, boolean isGet) {
- DexClass holder = appView.definitionFor(field.field.holder);
+ DexClass holder = definitionFor(field.field.holder);
assert holder != null;
- DexMethod bridgeMethod = computeFieldBridge(field, isGet, appView);
+ DexMethod bridgeMethod = computeFieldBridge(field, isGet);
if (holderRequiresBridge(holder)) {
return bridgeMethod;
}
// The map is used to avoid creating multiple times the bridge
// and remembers the bridges to add.
- Map<DexEncodedField, DexEncodedMethod> fieldMap = isGet ? getFieldBridges : putFieldBridges;
+ Map<DexField, DexEncodedMethod> fieldMap = isGet ? getFieldBridges : putFieldBridges;
fieldMap.computeIfAbsent(
- field,
+ field.field,
k ->
DexEncodedMethod.createFieldAccessorBridge(
new DexFieldWithAccess(field, isGet), holder, bridgeMethod));
@@ -364,14 +347,14 @@
DexMethod ensureInvokeBridge(DexEncodedMethod method) {
// We add bridges only when targeting other nest members.
- DexClass holder = appView.definitionFor(method.method.holder);
+ DexClass holder = definitionFor(method.method.holder);
assert holder != null;
DexMethod bridgeMethod;
if (method.isInstanceInitializer()) {
nestConstructorUsed = true;
- bridgeMethod = computeInitializerBridge(method.method, appView, nestConstructor.type);
+ bridgeMethod = computeInitializerBridge(method.method);
} else {
- bridgeMethod = computeMethodBridge(method, appView);
+ bridgeMethod = computeMethodBridge(method);
}
if (holderRequiresBridge(holder)) {
return bridgeMethod;
@@ -379,26 +362,31 @@
// The map is used to avoid creating multiple times the bridge
// and remembers the bridges to add.
bridges.computeIfAbsent(
- method,
+ method.method,
k ->
method.isInstanceInitializer()
? method.toInitializerForwardingBridge(holder, bridgeMethod)
- : method.toStaticForwardingBridge(holder, computeMethodBridge(method, appView)));
+ : method.toStaticForwardingBridge(holder, computeMethodBridge(method)));
return bridgeMethod;
}
protected class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
private final DexClass currentClass;
+ private DexMethod context;
NestBasedAccessDesugaringUseRegistry(DexClass currentClass) {
super(appView.options().itemFactory);
this.currentClass = currentClass;
}
- private boolean registerInvoke(DexMethod method) {
- DexEncodedMethod encodedMethod = appView.definitionFor(method);
- if (encodedMethod != null && invokeRequiresRewriting(encodedMethod, currentClass, appView)) {
+ public void setContext(DexMethod context) {
+ this.context = context;
+ }
+
+ private boolean registerInvoke(DexMethod method, Invoke.Type invokeType) {
+ DexEncodedMethod encodedMethod = definitionFor(method, context, invokeType);
+ if (encodedMethod != null && invokeRequiresRewriting(encodedMethod, currentClass)) {
ensureInvokeBridge(encodedMethod);
return true;
}
@@ -406,9 +394,8 @@
}
private boolean registerFieldAccess(DexField field, boolean isGet) {
- DexEncodedField encodedField = appView.definitionFor(field);
- if (encodedField != null
- && fieldAccessRequiresRewriting(encodedField, currentClass, appView)) {
+ DexEncodedField encodedField = definitionFor(field);
+ if (encodedField != null && fieldAccessRequiresRewriting(encodedField, currentClass)) {
ensureFieldAccessBridge(encodedField, isGet);
return true;
}
@@ -419,24 +406,24 @@
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, Invoke.Type.VIRTUAL);
}
@Override
public boolean registerInvokeDirect(DexMethod method) {
- return registerInvoke(method);
+ return registerInvoke(method, Invoke.Type.DIRECT);
}
@Override
public boolean registerInvokeStatic(DexMethod method) {
- return registerInvoke(method);
+ return registerInvoke(method, Invoke.Type.STATIC);
}
@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, Invoke.Type.INTERFACE);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
index c782edf..21d9b9f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
@@ -1,9 +1,6 @@
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.DexEncodedField;
-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.DexType;
@@ -11,54 +8,74 @@
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
import com.android.tools.r8.ir.code.Invoke;
import com.google.common.collect.ImmutableMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
public class NestedPrivateMethodLense extends NestedGraphLense {
- private final AppView<?> appView;
// Map from nestHost to nest members including nest hosts
private final DexType nestConstructorType;
+ private final Map<DexField, DexMethod> getFieldMap;
+ private final Map<DexField, DexMethod> putFieldMap;
- public NestedPrivateMethodLense(
+ NestedPrivateMethodLense(
AppView<?> appView,
DexType nestConstructorType,
+ Map<DexMethod, DexMethod> methodMap,
+ Map<DexField, DexMethod> getFieldMap,
+ Map<DexField, DexMethod> putFieldMap,
GraphLense previousLense) {
super(
ImmutableMap.of(),
- ImmutableMap.of(),
+ methodMap,
ImmutableMap.of(),
null,
null,
previousLense,
appView.dexItemFactory());
- this.appView = appView;
+ // No concurrent maps here, we do not want synchronization overhead.
+ assert methodMap instanceof IdentityHashMap;
+ assert getFieldMap instanceof IdentityHashMap;
+ assert putFieldMap instanceof IdentityHashMap;
+ this.getFieldMap = getFieldMap;
+ this.putFieldMap = putFieldMap;
this.nestConstructorType = nestConstructorType;
}
- private DexMethod lookupFieldForMethod(DexField field, DexMethod context, boolean isGet) {
- DexEncodedField encodedField = appView.definitionFor(field);
- DexClass contextClass = appView.definitionFor(context.holder);
- assert contextClass != null;
- if (encodedField != null
- && NestBasedAccessDesugaring.fieldAccessRequiresRewriting(
- encodedField, contextClass, appView)) {
- return NestBasedAccessDesugaring.computeFieldBridge(encodedField, isGet, appView);
+ private DexMethod lookupFieldForMethod(
+ DexField field, DexMethod context, Map<DexField, DexMethod> map) {
+ assert context != null;
+ DexMethod bridge = map.get(field);
+ if (bridge != null && bridge.holder != context.holder) {
+ return bridge;
}
return null;
}
@Override
public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
- return lookupFieldForMethod(field, context, true);
+ assert previousLense.lookupGetFieldForMethod(field, context) == null;
+ return lookupFieldForMethod(field, context, getFieldMap);
}
@Override
public DexMethod lookupPutFieldForMethod(DexField field, DexMethod context) {
- return lookupFieldForMethod(field, context, false);
+ assert previousLense.lookupPutFieldForMethod(field, context) == null;
+ return lookupFieldForMethod(field, context, putFieldMap);
}
@Override
public boolean isContextFreeForMethods() {
- return false;
+ return methodMap.isEmpty();
+ }
+
+ @Override
+ public boolean isContextFreeForMethod(DexMethod method) {
+ if (!previousLense.isContextFreeForMethod(method)) {
+ return false;
+ }
+ DexMethod previous = previousLense.lookupMethod(method);
+ return !methodMap.containsKey(previous);
}
// This is true because mappings specific to this class can be filled.
@@ -67,20 +84,24 @@
return true;
}
- @Override
- public RewrittenPrototypeDescription lookupPrototypeChanges(DexMethod method) {
+ private boolean isConstructorBridge(DexMethod method) {
DexType[] parameters = method.proto.parameters.values;
if (parameters.length == 0) {
- return previousLense.lookupPrototypeChanges(method);
+ return false;
}
DexType lastParameterType = parameters[parameters.length - 1];
- if (lastParameterType == nestConstructorType) {
- // This is an access bridge for a constructor that has been synthesized during
- // nest-based access desugaring.
+ return lastParameterType == nestConstructorType;
+ }
+
+ @Override
+ public RewrittenPrototypeDescription lookupPrototypeChanges(DexMethod method) {
+ if (isConstructorBridge(method)) {
+ // TODO (b/132767654): Try to write a test which breaks that assertion.
assert previousLense.lookupPrototypeChanges(method).isEmpty();
return RewrittenPrototypeDescription.none().withExtraNullParameter();
+ } else {
+ return previousLense.lookupPrototypeChanges(method);
}
- return previousLense.lookupPrototypeChanges(method);
}
@Override
@@ -91,29 +112,18 @@
? originalMethodSignatures.getOrDefault(context, context)
: context;
GraphLenseLookupResult previous = previousLense.lookupMethod(method, previousContext, type);
- if (context == null) {
+ DexMethod bridge = methodMap.get(previous.getMethod());
+ if (bridge == null) {
return previous;
}
- DexEncodedMethod encodedMethod = appView.definitionFor(method);
- DexClass contextClass = appView.definitionFor(context.holder);
- assert contextClass != null;
- if (encodedMethod == null
- || !NestBasedAccessDesugaring.invokeRequiresRewriting(
- encodedMethod, contextClass, appView)) {
+ assert context != null : "Guaranteed by isContextFreeForMethod";
+ if (bridge.holder == context.holder) {
return previous;
}
- DexMethod bridge;
- Invoke.Type invokeType;
- if (encodedMethod.isInstanceInitializer()) {
- bridge =
- NestBasedAccessDesugaring.computeInitializerBridge(method, appView, nestConstructorType);
- invokeType = Invoke.Type.DIRECT;
- } else {
- bridge = NestBasedAccessDesugaring.computeMethodBridge(encodedMethod, appView);
- invokeType = Invoke.Type.STATIC;
+ if (isConstructorBridge(bridge)) {
+ return new GraphLenseLookupResult(bridge, Invoke.Type.DIRECT);
}
-
- return new GraphLenseLookupResult(bridge, invokeType);
+ return new GraphLenseLookupResult(bridge, Invoke.Type.STATIC);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
index 977c7d9..1526505 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
@@ -2,6 +2,9 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.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;
@@ -10,18 +13,22 @@
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
// 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).
+// - Computes all the live nests reachable from Program Classes (Sequential), each time a
+// nest is computed, its processing starts concurrently.
+// - Add bridges to be processed by further passes and create the maps
+// for the lens (Sequential)
public class R8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
- private boolean atLeast1Nest = false;
+ private final Map<DexMethod, DexMethod> lensBridges = new IdentityHashMap<>();
+ private final Map<DexField, DexMethod> lensGetFieldBridges = new IdentityHashMap<>();
+ private final Map<DexField, DexMethod> lensPutFieldBridges = new IdentityHashMap<>();
public R8NestBasedAccessDesugaring(AppView<?> appView) {
super(appView);
@@ -32,12 +39,43 @@
return appView.graphLense();
}
computeAndProcessNestsConcurrently(executorService);
- if (!atLeast1Nest) {
- // Common path when no class file was generated by JDK11+.
+ addDeferredBridgesAndMapMethods();
+ if (nothingToMap()) {
return appView.graphLense();
}
- addDeferredBridges();
- return new NestedPrivateMethodLense(appView, getNestConstructorType(), appView.graphLense());
+ return new NestedPrivateMethodLense(
+ appView,
+ getNestConstructorType(),
+ lensBridges,
+ lensGetFieldBridges,
+ lensPutFieldBridges,
+ appView.graphLense());
+ }
+
+ private boolean nothingToMap() {
+ return lensBridges.isEmpty() && lensGetFieldBridges.isEmpty() && lensPutFieldBridges.isEmpty();
+ }
+
+ private void addDeferredBridgesAndMapMethods() {
+ // Here we add the bridges and we fill the lens map.
+ // The lens map are different than the original map since
+ // they refer DexMethod and not DexEncodedMethod (so they can be long lived without issues),
+ // and since they do not require synchronization (they are only read in the lens).
+ // We cannot easily do this concurrently since methods are added to classes.
+ addDeferredBridgesAndMapMethods(bridges, lensBridges);
+ addDeferredBridgesAndMapMethods(getFieldBridges, lensGetFieldBridges);
+ addDeferredBridgesAndMapMethods(putFieldBridges, lensPutFieldBridges);
+ }
+
+ private <E> void addDeferredBridgesAndMapMethods(
+ Map<E, DexEncodedMethod> bridges, Map<E, DexMethod> map) {
+ for (Map.Entry<E, DexEncodedMethod> entry : bridges.entrySet()) {
+ DexClass holder = definitionFor(entry.getValue().method.holder);
+ assert holder != null && holder.isProgramClass();
+ holder.asProgramClass().addMethod(entry.getValue());
+ map.put(entry.getKey(), entry.getValue().method);
+ }
+ bridges.clear();
}
private void computeAndProcessNestsConcurrently(ExecutorService executorService)
@@ -53,7 +91,6 @@
DexType hostType = clazz.getNestHost();
if (!nestHosts.contains(hostType)) {
nestHosts.add(hostType);
- atLeast1Nest = true;
futures.add(asyncProcessNest(clazz, executorService));
}
}