Extend profile rewriting to nest based access desugaring
Since the synthetics from nest based access desugaring are created in the prepare desugaring step, this CL extends the prepare() step with the CfInstructionDesugaringEventConsumer, and performs the relevant callbacks to it.
Additionally, this CL makes some minor cleanups to the nest based access desugaring. In particular this CL clearly separates the parts of nest based access desugaring that creates methods (the prepare step) from the parts that don't (the instruction rewrite step). As an example, prior to this CL, the method getMethodBridgeReference() had logic to create a synthetic constructor argument class using synthetic items. Moreover, D8 specific handling of classpath accesses to the program is moved to D8NestBasedAccessDesugaring.
Bug: b/265729283
Change-Id: I6f8da35fffad467793a9fa5f744204b252c06e8f
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 1ccb991..c1bd17b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -125,7 +125,11 @@
.build();
}
- converter.prepareDesugaringForD8(executorService);
+ CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumerForPrepareStep =
+ CfInstructionDesugaringEventConsumer.createForD8(appView, resultBuilder, methodProcessor);
+ converter.prepareDesugaringForD8(
+ instructionDesugaringEventConsumerForPrepareStep, executorService);
+ assert instructionDesugaringEventConsumerForPrepareStep.verifyNothingToFinalize();
// When adding nest members to the wave we must do so deterministically.
Deque<List<DexProgramClass>> nestProcessingWaves = getDeterministicNestWaves(classes);
@@ -144,19 +148,22 @@
methodProcessor.addScheduled(clazz);
}
- CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer =
+ CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumerForWave =
CfInstructionDesugaringEventConsumer.createForD8(appView, resultBuilder, methodProcessor);
// Process the wave and wait for all IR processing to complete.
methodProcessor.newWave();
checkWaveDeterminism(wave);
ThreadUtils.processItems(
- wave, clazz -> convertClass(clazz, instructionDesugaringEventConsumer), executorService);
+ wave,
+ clazz -> convertClass(clazz, instructionDesugaringEventConsumerForWave),
+ executorService);
methodProcessor.awaitMethodProcessing();
// Finalize the desugaring of the processed classes. This may require processing (and
// reprocessing) of some methods.
- List<ProgramMethod> needsProcessing = instructionDesugaringEventConsumer.finalizeDesugaring();
+ List<ProgramMethod> needsProcessing =
+ instructionDesugaringEventConsumerForWave.finalizeDesugaring();
if (!needsProcessing.isEmpty()) {
// Create a new processor context to ensure unique method processing contexts.
methodProcessor.newWave();
@@ -170,16 +177,16 @@
if (definition.isProcessed()) {
definition.markNotProcessed();
}
- methodProcessor.processMethod(method, instructionDesugaringEventConsumer);
+ methodProcessor.processMethod(method, instructionDesugaringEventConsumerForWave);
if (interfaceProcessor != null) {
- interfaceProcessor.processMethod(method, instructionDesugaringEventConsumer);
+ interfaceProcessor.processMethod(method, instructionDesugaringEventConsumerForWave);
}
},
executorService);
// Verify there is nothing to finalize once method processing finishes.
methodProcessor.awaitMethodProcessing();
- assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
+ assert instructionDesugaringEventConsumerForWave.verifyNothingToFinalize();
}
if (!nestProcessingWaves.isEmpty()) {
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 547fef4..ccb3126 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
@@ -471,7 +471,9 @@
DesugaredLibraryAPIConverter::generateTrackingWarnings);
}
- public void prepareDesugaringForD8(ExecutorService executorService) throws ExecutionException {
+ public void prepareDesugaringForD8(
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer, ExecutorService executorService)
+ throws ExecutionException {
// Prepare desugaring by collecting all the synthetic methods required on program classes.
ProgramAdditions programAdditions = new ProgramAdditions();
ThreadUtils.processItems(
@@ -479,7 +481,8 @@
clazz -> {
clazz.forEachProgramMethodMatching(
method -> method.hasCode() && method.getCode().isCfCode(),
- method -> instructionDesugaring.prepare(method, programAdditions));
+ method ->
+ instructionDesugaring.prepare(method, desugaringEventConsumer, programAdditions));
},
executorService);
programAdditions.apply(executorService);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
index 8f1ddeb..0d454d6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
@@ -26,7 +26,10 @@
* synthetic items can be added and the instruction stream can be altered, but program methods
* cannot be added.
*/
- default void prepare(ProgramMethod method, ProgramAdditions programAdditions) {
+ default void prepare(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramAdditions programAdditions) {
// Default prepare is to do nothing.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index c457ace..90eda8a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -44,7 +44,10 @@
return EmptyCfInstructionDesugaringCollection.getInstance();
}
- public abstract void prepare(ProgramMethod method, ProgramAdditions programAdditions);
+ public abstract void prepare(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramAdditions programAdditions);
public abstract void scan(
ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index edead3e..ab2f31f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
@@ -202,18 +203,30 @@
}
@Override
- public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
- assert false;
+ public void acceptNestConstructorBridge(
+ ProgramMethod target,
+ ProgramMethod bridge,
+ DexProgramClass argumentClass,
+ DexClassAndMethod context) {
+ // Intentionally empty.
}
@Override
- public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
- assert false;
+ public void acceptNestFieldGetBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+ // Intentionally empty.
}
@Override
- public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
- assert false;
+ public void acceptNestFieldPutBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptNestMethodBridge(
+ ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) {
+ // Intentionally empty.
}
@Override
@@ -478,18 +491,30 @@
}
@Override
- public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
- assert false;
+ public void acceptNestConstructorBridge(
+ ProgramMethod target,
+ ProgramMethod bridge,
+ DexProgramClass argumentClass,
+ DexClassAndMethod context) {
+ // Intentionally empty.
}
@Override
- public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
- assert false;
+ public void acceptNestFieldGetBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+ // Intentionally empty.
}
@Override
- public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
- assert false;
+ public void acceptNestFieldPutBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptNestMethodBridge(
+ ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) {
+ // Intentionally empty.
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index e7a1587..64bc96e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -30,7 +30,10 @@
}
@Override
- public void prepare(ProgramMethod method, ProgramAdditions additionalProgramMethods) {
+ public void prepare(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramAdditions additionalProgramMethods) {
// Intentionally empty.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 20e5a46..97216e8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -187,9 +187,12 @@
}
@Override
- public void prepare(ProgramMethod method, ProgramAdditions programAdditions) {
+ public void prepare(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramAdditions programAdditions) {
ensureCfCode(method);
- desugarings.forEach(d -> d.prepare(method, programAdditions));
+ desugarings.forEach(d -> d.prepare(method, eventConsumer, programAdditions));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java b/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java
index cf57453..932ea7c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java
@@ -5,48 +5,47 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.ThreadUtils;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
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.BiConsumer;
import java.util.function.Supplier;
-public class ProgramAdditions implements BiConsumer<DexMember<?, ?>, Supplier<ProgramMethod>> {
- private final Set<DexReference> added = Sets.newConcurrentHashSet();
- private final Map<DexProgramClass, List<DexEncodedMethod>> additions = new ConcurrentHashMap<>();
+public class ProgramAdditions {
- @Override
- public synchronized void accept(
- DexMember<?, ?> reference, Supplier<ProgramMethod> programMethodSupplier) {
- if (added.add(reference)) {
- ProgramMethod method = programMethodSupplier.get();
- List<DexEncodedMethod> methods =
- additions.computeIfAbsent(method.getHolder(), k -> new ArrayList<>());
- synchronized (methods) {
- assert !methods.contains(method.getDefinition());
- assert method.getHolder().lookupProgramMethod(method.getReference()) == null;
- methods.add(method.getDefinition());
- }
- }
+ private final Map<DexType, Map<DexMethod, ProgramMethod>> additions = new ConcurrentHashMap<>();
+
+ public ProgramMethod ensureMethod(
+ DexMethod methodReference, Supplier<ProgramMethod> programMethodSupplier) {
+ Map<DexMethod, ProgramMethod> classAdditions =
+ additions.computeIfAbsent(
+ methodReference.getHolderType(), key -> new ConcurrentHashMap<>());
+ return classAdditions.computeIfAbsent(
+ methodReference,
+ key -> {
+ ProgramMethod method = programMethodSupplier.get();
+ assert method.getHolder().lookupProgramMethod(method.getReference()) == null;
+ return method;
+ });
}
public void apply(ExecutorService executorService) throws ExecutionException {
ThreadUtils.processMap(
additions,
- (clazz, methods) -> {
- methods.sort(Comparator.comparing(DexEncodedMethod::getReference));
- clazz.getMethodCollection().addDirectMethods(methods);
+ (holderType, methodMap) -> {
+ DexProgramClass holder = methodMap.values().iterator().next().getHolder();
+ List<DexEncodedMethod> newDirectMethods = new ArrayList<>();
+ methodMap.values().forEach(method -> newDirectMethods.add(method.getDefinition()));
+ newDirectMethods.sort(Comparator.comparing(DexEncodedMethod::getReference));
+ holder.getMethodCollection().addDirectMethods(newDirectMethods);
},
executorService);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
index bde95b8..34324ff 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexClasspathClass;
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.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
@@ -94,17 +95,29 @@
new NestBasedAccessDesugaringEventConsumer() {
@Override
- public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
+ public void acceptNestConstructorBridge(
+ ProgramMethod target,
+ ProgramMethod bridge,
+ DexProgramClass argumentClass,
+ DexClassAndMethod context) {
methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
}
@Override
- public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
+ public void acceptNestFieldGetBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
}
@Override
- public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
+ public void acceptNestFieldPutBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
+ }
+
+ @Override
+ public void acceptNestMethodBridge(
+ ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) {
methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
}
};
@@ -132,68 +145,151 @@
this.eventConsumer = eventConsumer;
}
- private void registerFieldAccess(DexField reference, boolean isGet) {
+ private void registerFieldAccessFromClasspath(DexField reference, boolean isGet) {
DexClassAndField field =
reference.lookupMemberOnClass(appView.definitionForHolder(reference));
if (field != null && needsDesugaring(field, getContext())) {
- ensureFieldAccessBridge(field, isGet, eventConsumer);
+ ensureFieldAccessBridgeFromClasspathAccess(field, isGet, eventConsumer);
}
}
- private void registerInvoke(DexMethod reference) {
+ private void ensureFieldAccessBridgeFromClasspathAccess(
+ DexClassAndField field,
+ boolean isGet,
+ NestBasedAccessDesugaringEventConsumer eventConsumer) {
+ if (field.isProgramField()) {
+ ensureFieldAccessBridgeFromClasspathAccess(field.asProgramField(), isGet, eventConsumer);
+ } else if (field.isClasspathField()) {
+ // Intentionally empty.
+ } else {
+ assert field.isLibraryField();
+ throw reportIncompleteNest(field.asLibraryField());
+ }
+ }
+
+ private void ensureFieldAccessBridgeFromClasspathAccess(
+ ProgramField field, boolean isGet, NestBasedAccessDesugaringEventConsumer eventConsumer) {
+ DexMethod bridgeReference = getFieldAccessBridgeReference(field, isGet);
+ synchronized (field.getHolder().getMethodCollection()) {
+ if (field.getHolder().lookupMethod(bridgeReference) == null) {
+ ProgramMethod bridge =
+ AccessBridgeFactory.createFieldAccessorBridge(bridgeReference, field, isGet);
+ bridge.getHolder().addDirectMethod(bridge.getDefinition());
+ if (isGet) {
+ eventConsumer.acceptNestFieldGetBridge(field, bridge, getContext());
+ } else {
+ eventConsumer.acceptNestFieldPutBridge(field, bridge, getContext());
+ }
+ }
+ }
+ }
+
+ private void registerInvokeFromClasspath(DexMethod reference) {
if (!reference.getHolderType().isClassType()) {
return;
}
DexClassAndMethod method =
reference.lookupMemberOnClass(appView.definitionForHolder(reference));
if (method != null && needsDesugaring(method, getContext())) {
- ensureMethodBridge(method, eventConsumer);
+ ensureConstructorOrMethodBridgeFromClasspathAccess(method, eventConsumer);
+ }
+ }
+
+ // This is only used for generating bridge methods for class path references.
+ private void ensureConstructorOrMethodBridgeFromClasspathAccess(
+ DexClassAndMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
+ if (method.isProgramMethod()) {
+ if (method.getDefinition().isInstanceInitializer()) {
+ ensureConstructorBridgeFromClasspathAccess(method.asProgramMethod(), eventConsumer);
+ } else {
+ ensureMethodBridgeFromClasspathAccess(method.asProgramMethod(), eventConsumer);
+ }
+ } else if (method.isClasspathMethod()) {
+ if (method.getDefinition().isInstanceInitializer()) {
+ ensureConstructorArgumentClass(method);
+ }
+ } else {
+ assert method.isLibraryMethod();
+ throw reportIncompleteNest(method.asLibraryMethod());
+ }
+ }
+
+ private void ensureConstructorBridgeFromClasspathAccess(
+ ProgramMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
+ assert method.getDefinition().isInstanceInitializer();
+ DexProgramClass constructorArgumentClass =
+ ensureConstructorArgumentClass(method).asProgramClass();
+ DexMethod bridgeReference = getConstructorBridgeReference(method, constructorArgumentClass);
+ synchronized (method.getHolder().getMethodCollection()) {
+ if (method.getHolder().lookupMethod(bridgeReference) == null) {
+ ProgramMethod bridge =
+ AccessBridgeFactory.createInitializerAccessorBridge(
+ bridgeReference, method, dexItemFactory);
+ bridge.getHolder().addDirectMethod(bridge.getDefinition());
+ eventConsumer.acceptNestConstructorBridge(
+ method, bridge, constructorArgumentClass, getContext());
+ }
+ }
+ }
+
+ private void ensureMethodBridgeFromClasspathAccess(
+ ProgramMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
+ assert !method.getDefinition().isInstanceInitializer();
+ DexMethod bridgeReference = getMethodBridgeReference(method);
+ synchronized (method.getHolder().getMethodCollection()) {
+ if (method.getHolder().lookupMethod(bridgeReference) == null) {
+ ProgramMethod bridge =
+ AccessBridgeFactory.createMethodAccessorBridge(
+ bridgeReference, method, dexItemFactory);
+ bridge.getHolder().addDirectMethod(bridge.getDefinition());
+ eventConsumer.acceptNestMethodBridge(method, bridge, getContext());
+ }
}
}
@Override
public void registerInvokeDirect(DexMethod method) {
- registerInvoke(method);
+ registerInvokeFromClasspath(method);
}
@Override
public void registerInvokeInterface(DexMethod method) {
- registerInvoke(method);
+ registerInvokeFromClasspath(method);
}
@Override
public void registerInvokeStatic(DexMethod method) {
- registerInvoke(method);
+ registerInvokeFromClasspath(method);
}
@Override
public void registerInvokeSuper(DexMethod method) {
- registerInvoke(method);
+ registerInvokeFromClasspath(method);
}
@Override
public void registerInvokeVirtual(DexMethod method) {
- registerInvoke(method);
+ registerInvokeFromClasspath(method);
}
@Override
public void registerInstanceFieldWrite(DexField field) {
- registerFieldAccess(field, false);
+ registerFieldAccessFromClasspath(field, false);
}
@Override
public void registerInstanceFieldRead(DexField field) {
- registerFieldAccess(field, true);
+ registerFieldAccessFromClasspath(field, true);
}
@Override
public void registerStaticFieldRead(DexField field) {
- registerFieldAccess(field, true);
+ registerFieldAccessFromClasspath(field, true);
}
@Override
public void registerStaticFieldWrite(DexField field) {
- registerFieldAccess(field, false);
+ registerFieldAccessFromClasspath(field, false);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index ad59051..c9c2c54 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMember;
@@ -48,6 +47,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
+import java.util.function.Function;
import org.objectweb.asm.Opcodes;
// NestBasedAccessDesugaring contains common code between the two subclasses
@@ -66,8 +66,8 @@
NEST_ACCESS_NAME_PREFIX + "sfput";
protected final AppView<?> appView;
- private final DexItemFactory dexItemFactory;
- private final Map<DexType, DexType> syntheticNestConstructorTypes = new ConcurrentHashMap<>();
+ protected final DexItemFactory dexItemFactory;
+ private final Map<DexType, DexClass> syntheticNestConstructorTypes = new ConcurrentHashMap<>();
NestBasedAccessDesugaring(AppView<?> appView) {
this.appView = appView;
@@ -140,7 +140,10 @@
}
@Override
- public void prepare(ProgramMethod method, ProgramAdditions programAdditions) {
+ public void prepare(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramAdditions programAdditions) {
method
.getDefinition()
.getCode()
@@ -155,12 +158,14 @@
field,
instruction.asFieldInstruction().isFieldGet(),
method,
+ eventConsumer,
programAdditions);
}
} else if (instruction.isInvoke()) {
DexMethod invokedMethod = instruction.asInvoke().getMethod();
if (needsDesugaring(invokedMethod, method)) {
- prepareDesugarMethodInstruction(invokedMethod, method, programAdditions);
+ prepareDesugarMethodInstruction(
+ invokedMethod, method, eventConsumer, programAdditions);
}
} else if (instruction.isInvokeDynamic()) {
// Starting from Java 17, lambda can use nest based access. We need to generate
@@ -173,7 +178,8 @@
DexMember<?, ?> member = lambdaDescriptor.implHandle.member;
if (needsDesugaring(member, method)) {
assert member.isDexMethod();
- prepareDesugarMethodInstruction(member.asDexMethod(), method, programAdditions);
+ prepareDesugarMethodInstruction(
+ member.asDexMethod(), method, eventConsumer, programAdditions);
}
}
}
@@ -181,43 +187,63 @@
}
private void prepareDesugarFieldInstruction(
- DexField field, boolean isGet, ProgramMethod context, ProgramAdditions programAdditions) {
+ DexField field,
+ boolean isGet,
+ ProgramMethod context,
+ NestBasedAccessDesugaringEventConsumer eventConsumer,
+ ProgramAdditions programAdditions) {
BridgeAndTarget<DexClassAndField> bridgeAndTarget =
bridgeAndTargetForDesugaring(field, isGet, context);
if (bridgeAndTarget == null || !bridgeAndTarget.shouldAddBridge()) {
return;
}
- programAdditions.accept(
- bridgeAndTarget.getBridge(),
- () ->
- AccessBridgeFactory.createFieldAccessorBridge(
- bridgeAndTarget.getBridge(), bridgeAndTarget.getTarget().asProgramField(), isGet));
+ ProgramField targetField = bridgeAndTarget.getTarget().asProgramField();
+ ProgramMethod bridgeMethod =
+ programAdditions.ensureMethod(
+ bridgeAndTarget.getBridge(),
+ () ->
+ AccessBridgeFactory.createFieldAccessorBridge(
+ bridgeAndTarget.getBridge(), targetField, isGet));
+ if (isGet) {
+ eventConsumer.acceptNestFieldGetBridge(targetField, bridgeMethod, context);
+ } else {
+ eventConsumer.acceptNestFieldPutBridge(targetField, bridgeMethod, context);
+ }
}
private void prepareDesugarMethodInstruction(
- DexMethod method, ProgramMethod context, ProgramAdditions programAdditions) {
+ DexMethod method,
+ ProgramMethod context,
+ NestBasedAccessDesugaringEventConsumer eventConsumer,
+ ProgramAdditions programAdditions) {
BridgeAndTarget<DexClassAndMethod> bridgeAndTarget =
- bridgeAndTargetForDesugaring(method, context);
+ bridgeAndTargetForDesugaring(method, context, this::ensureConstructorArgumentClass);
if (bridgeAndTarget == null || !bridgeAndTarget.shouldAddBridge()) {
return;
}
- programAdditions.accept(
- bridgeAndTarget.getBridge(),
- () ->
- bridgeAndTarget.getTarget().getDefinition().isInstanceInitializer()
- ? AccessBridgeFactory.createInitializerAccessorBridge(
- bridgeAndTarget.getBridge(),
- bridgeAndTarget.getTarget().asProgramMethod(),
- dexItemFactory)
- : AccessBridgeFactory.createMethodAccessorBridge(
- bridgeAndTarget.getBridge(),
- bridgeAndTarget.getTarget().asProgramMethod(),
- dexItemFactory));
+ ProgramMethod targetMethod = bridgeAndTarget.getTarget().asProgramMethod();
+ ProgramMethod bridgeMethod =
+ programAdditions.ensureMethod(
+ bridgeAndTarget.getBridge(),
+ () ->
+ targetMethod.getDefinition().isInstanceInitializer()
+ ? AccessBridgeFactory.createInitializerAccessorBridge(
+ bridgeAndTarget.getBridge(), targetMethod, dexItemFactory)
+ : AccessBridgeFactory.createMethodAccessorBridge(
+ bridgeAndTarget.getBridge(), targetMethod, dexItemFactory));
+ if (targetMethod.getDefinition().isInstanceInitializer()) {
+ DexProgramClass argumentClass = getConstructorArgumentClass(targetMethod).asProgramClass();
+ eventConsumer.acceptNestConstructorBridge(targetMethod, bridgeMethod, argumentClass, context);
+ } else {
+ eventConsumer.acceptNestMethodBridge(targetMethod, bridgeMethod, context);
+ }
}
private BridgeAndTarget<DexClassAndMethod> bridgeAndTargetForDesugaring(
- DexMethod method, ProgramMethod context) {
+ DexMethod method,
+ ProgramMethod context,
+ Function<DexClassAndMethod, DexClass> constructorArgumentClassProvider) {
if (!method.getHolderType().isClassType()) {
return null;
}
@@ -229,7 +255,14 @@
if (target == null || !needsDesugaring(target, context)) {
return null;
}
- return new BridgeAndTarget<>(getMethodBridgeReference(target), target);
+ DexMethod bridgeReference;
+ if (target.getDefinition().isInstanceInitializer()) {
+ DexClass constructorArgumentClass = constructorArgumentClassProvider.apply(target);
+ bridgeReference = getConstructorBridgeReference(target, constructorArgumentClass);
+ } else {
+ bridgeReference = getMethodBridgeReference(target);
+ }
+ return new BridgeAndTarget<>(bridgeReference, target);
}
private BridgeAndTarget<DexClassAndField> bridgeAndTargetForDesugaring(
@@ -301,20 +334,16 @@
CfInstructionDesugaringCollection desugaringCollection,
DexItemFactory dexItemFactory) {
if (instruction.isFieldInstruction()) {
- return desugarFieldInstruction(instruction.asFieldInstruction(), context, eventConsumer);
+ return desugarFieldInstruction(instruction.asFieldInstruction(), context);
}
if (instruction.isInvoke()) {
- return desugarInvokeInstruction(
- instruction.asInvoke(), localStackAllocator, context, eventConsumer);
+ return desugarInvokeInstruction(instruction.asInvoke(), localStackAllocator, context);
}
return null;
}
private List<CfInstruction> desugarFieldInstruction(
- CfFieldInstruction instruction,
- ProgramMethod context,
- NestBasedAccessDesugaringEventConsumer eventConsumer) {
-
+ CfFieldInstruction instruction, ProgramMethod context) {
BridgeAndTarget<DexClassAndField> bridgeAndTarget =
bridgeAndTargetForDesugaring(instruction.getField(), instruction.isFieldGet(), context);
if (bridgeAndTarget == null) {
@@ -332,14 +361,10 @@
}
private List<CfInstruction> desugarInvokeInstruction(
- CfInvoke invoke,
- LocalStackAllocator localStackAllocator,
- ProgramMethod context,
- NestBasedAccessDesugaringEventConsumer eventConsumer) {
+ CfInvoke invoke, LocalStackAllocator localStackAllocator, ProgramMethod context) {
DexMethod invokedMethod = invoke.getMethod();
-
BridgeAndTarget<DexClassAndMethod> bridgeAndTarget =
- bridgeAndTargetForDesugaring(invokedMethod, context);
+ bridgeAndTargetForDesugaring(invokedMethod, context, this::getConstructorArgumentClass);
if (bridgeAndTarget == null) {
return null;
}
@@ -360,45 +385,13 @@
new CfInvoke(Opcodes.INVOKESTATIC, bridgeAndTarget.getBridge(), invoke.isInterface()));
}
- private RuntimeException reportIncompleteNest(LibraryMember<?, ?> member) {
+ RuntimeException reportIncompleteNest(LibraryMember<?, ?> member) {
Nest nest = Nest.create(appView, member.getHolder());
assert nest != null : "Should be a compilation error if missing nest host on library class.";
throw appView.options().errorMissingNestMember(nest);
}
- DexMethod ensureFieldAccessBridge(
- DexClassAndField field, boolean isGet, NestBasedAccessDesugaringEventConsumer eventConsumer) {
- if (field.isProgramField()) {
- return ensureFieldAccessBridge(field.asProgramField(), isGet, eventConsumer);
- }
- if (field.isClasspathField()) {
- return getFieldAccessBridgeReference(field, isGet);
- }
- assert field.isLibraryField();
- throw reportIncompleteNest(field.asLibraryField());
- }
-
- private DexMethod ensureFieldAccessBridge(
- ProgramField field, boolean isGet, NestBasedAccessDesugaringEventConsumer eventConsumer) {
- DexMethod bridgeReference = getFieldAccessBridgeReference(field, isGet);
- synchronized (field.getHolder().getMethodCollection()) {
- ProgramMethod bridge = field.getHolder().lookupProgramMethod(bridgeReference);
- if (bridge == null) {
- bridge = AccessBridgeFactory.createFieldAccessorBridge(bridgeReference, field, isGet);
- bridge.getHolder().addDirectMethod(bridge.getDefinition());
- if (eventConsumer != null) {
- if (isGet) {
- eventConsumer.acceptNestFieldGetBridge(field, bridge);
- } else {
- eventConsumer.acceptNestFieldPutBridge(field, bridge);
- }
- }
- }
- return bridge.getReference();
- }
- }
-
- private DexMethod getFieldAccessBridgeReference(DexClassAndField field, boolean isGet) {
+ DexMethod getFieldAccessBridgeReference(DexClassAndField field, boolean isGet) {
int bridgeParameterCount =
BooleanUtils.intValue(!field.getAccessFlags().isStatic()) + BooleanUtils.intValue(!isGet);
DexType[] parameters = new DexType[bridgeParameterCount];
@@ -428,72 +421,47 @@
return dexItemFactory.createString(prefix + field.getName().toString());
}
- // This is only used for generating bridge methods for class path references.
- DexMethod ensureMethodBridge(
- DexClassAndMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
- if (method.isProgramMethod()) {
- return ensureMethodBridge(method.asProgramMethod(), eventConsumer);
- }
- if (method.isClasspathMethod()) {
- return getMethodBridgeReference(method);
- }
- assert method.isLibraryMethod();
- throw reportIncompleteNest(method.asLibraryMethod());
+ private DexClass getConstructorArgumentClass(DexClassAndMethod constructor) {
+ return syntheticNestConstructorTypes.get(constructor.getHolderType());
}
- private DexMethod ensureMethodBridge(
- ProgramMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
- DexMethod bridgeReference = getMethodBridgeReference(method);
- synchronized (method.getHolder().getMethodCollection()) {
- ProgramMethod bridge = method.getHolder().lookupProgramMethod(bridgeReference);
- if (bridge == null) {
- DexEncodedMethod definition = method.getDefinition();
- bridge =
- definition.isInstanceInitializer()
- ? AccessBridgeFactory.createInitializerAccessorBridge(
- bridgeReference, method, dexItemFactory)
- : AccessBridgeFactory.createMethodAccessorBridge(
- bridgeReference, method, dexItemFactory);
- bridge.getHolder().addDirectMethod(bridge.getDefinition());
- if (eventConsumer != null) {
- eventConsumer.acceptNestMethodBridge(method, bridge);
- }
- }
- }
- return bridgeReference;
+ DexClass ensureConstructorArgumentClass(DexClassAndMethod constructor) {
+ assert constructor.getDefinition().isInstanceInitializer();
+ return syntheticNestConstructorTypes.computeIfAbsent(
+ constructor.getHolderType(),
+ holder -> {
+ if (constructor.isProgramMethod()) {
+ return appView
+ .getSyntheticItems()
+ .createFixedClass(
+ kinds -> kinds.INIT_TYPE_ARGUMENT,
+ constructor.asProgramMethod().getHolder(),
+ appView,
+ builder -> {});
+ } else {
+ assert constructor.isClasspathMethod();
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClasspathClass(
+ kinds -> kinds.INIT_TYPE_ARGUMENT,
+ constructor.asClasspathMethod().getHolder(),
+ appView,
+ ignored -> {},
+ ignored -> {});
+ }
+ });
}
- private DexMethod getMethodBridgeReference(DexClassAndMethod method) {
- if (method.getDefinition().isInstanceInitializer()) {
- DexType nestConstructorType =
- syntheticNestConstructorTypes.computeIfAbsent(
- method.getHolderType(),
- holder -> {
- if (method.isProgramMethod()) {
- return appView
- .getSyntheticItems()
- .createFixedClass(
- kinds -> kinds.INIT_TYPE_ARGUMENT,
- method.asProgramMethod().getHolder(),
- appView,
- builder -> {})
- .getType();
- } else {
- assert method.isClasspathMethod();
- return appView
- .getSyntheticItems()
- .ensureFixedClasspathClass(
- kinds -> kinds.INIT_TYPE_ARGUMENT,
- method.asClasspathMethod().getHolder(),
- appView,
- ignored -> {},
- ignored -> {})
- .getType();
- }
- });
- DexProto newProto = dexItemFactory.appendTypeToProto(method.getProto(), nestConstructorType);
- return method.getReference().withProto(newProto, dexItemFactory);
- }
+ DexMethod getConstructorBridgeReference(
+ DexClassAndMethod method, DexClass constructorArgumentClass) {
+ assert method.getDefinition().isInstanceInitializer();
+ DexProto newProto =
+ dexItemFactory.appendTypeToProto(method.getProto(), constructorArgumentClass.getType());
+ return method.getReference().withProto(newProto, dexItemFactory);
+ }
+
+ DexMethod getMethodBridgeReference(DexClassAndMethod method) {
+ assert !method.getDefinition().isInstanceInitializer();
DexProto proto =
method.getAccessFlags().isStatic()
? method.getProto()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java
index b5afce1..dac3668 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java
@@ -4,14 +4,25 @@
package com.android.tools.r8.ir.desugar.nest;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
public interface NestBasedAccessDesugaringEventConsumer {
- void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge);
+ void acceptNestConstructorBridge(
+ ProgramMethod target,
+ ProgramMethod bridge,
+ DexProgramClass argumentClass,
+ DexClassAndMethod context);
- void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge);
+ void acceptNestFieldGetBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context);
- void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge);
+ void acceptNestFieldPutBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context);
+
+ void acceptNestMethodBridge(
+ ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index 81736ab..afae01d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -96,7 +96,10 @@
}
@Override
- public void prepare(ProgramMethod method, ProgramAdditions programAdditions) {
+ public void prepare(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramAdditions programAdditions) {
CfCode cfCode = method.getDefinition().getCode().asCfCode();
for (CfInstruction instruction : cfCode.getInstructions()) {
if (instruction.isInvokeDynamic() && needsDesugaring(instruction, method)) {
@@ -253,7 +256,7 @@
DexProgramClass clazz = recordInvokeDynamic.getRecordClass();
DexMethod method = equalsRecordMethod(clazz.type);
assert clazz.lookupProgramMethod(method) == null;
- programAdditions.accept(
+ programAdditions.ensureMethod(
method, () -> synthesizeEqualsRecordMethod(clazz, getFieldsAsObjects, method));
return method;
}
@@ -263,7 +266,7 @@
DexProgramClass clazz = recordInvokeDynamic.getRecordClass();
DexMethod method = getFieldsAsObjectsMethod(clazz.type);
assert clazz.lookupProgramMethod(method) == null;
- programAdditions.accept(
+ programAdditions.ensureMethod(
method,
() -> synthesizeGetFieldsAsObjectsMethod(clazz, recordInvokeDynamic.getFields(), method));
return method;
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
index 7e44875..738d12e 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.profile.art.rewriting;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
@@ -118,18 +119,39 @@
}
@Override
- public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
- parent.acceptNestFieldGetBridge(target, bridge);
+ public void acceptNestConstructorBridge(
+ ProgramMethod target,
+ ProgramMethod bridge,
+ DexProgramClass argumentClass,
+ DexClassAndMethod context) {
+ assert context.isProgramMethod();
+ additionsCollection.addRulesIfContextIsInProfile(
+ context.asProgramMethod(), argumentClass, bridge);
+ parent.acceptNestConstructorBridge(target, bridge, argumentClass, context);
}
@Override
- public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
- parent.acceptNestFieldPutBridge(target, bridge);
+ public void acceptNestFieldGetBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+ assert context.isProgramMethod();
+ additionsCollection.addRulesIfContextIsInProfile(context.asProgramMethod(), bridge);
+ parent.acceptNestFieldGetBridge(target, bridge, context);
}
@Override
- public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
- parent.acceptNestMethodBridge(target, bridge);
+ public void acceptNestFieldPutBridge(
+ ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+ assert context.isProgramMethod();
+ additionsCollection.addRulesIfContextIsInProfile(context.asProgramMethod(), bridge);
+ parent.acceptNestFieldPutBridge(target, bridge, context);
+ }
+
+ @Override
+ public void acceptNestMethodBridge(
+ ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) {
+ assert context.isProgramMethod();
+ additionsCollection.addRulesIfContextIsInProfile(context.asProgramMethod(), bridge);
+ parent.acceptNestMethodBridge(target, bridge, context);
}
@Override
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 11a9fb6..ca0f9b5 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -4108,7 +4108,7 @@
ProgramAdditions programAdditions = new ProgramAdditions();
ThreadUtils.processItems(
pendingCodeDesugaring,
- method -> desugaring.prepare(method, programAdditions),
+ method -> desugaring.prepare(method, eventConsumer, programAdditions),
executorService);
programAdditions.apply(executorService);
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
index e376dbc..93f9488 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -93,6 +93,12 @@
ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
assertThat(nestMemberClassSubject, isPresent());
+ MethodSubject instanceInitializerWithSyntheticArgumentSubject =
+ nestMemberClassSubject.uniqueInstanceInitializer();
+ assertThat(
+ instanceInitializerWithSyntheticArgumentSubject,
+ notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
ClassSubject syntheticConstructorArgumentClassSubject =
inspector.clazz(
SyntheticItemsTestUtils.syntheticNestConstructorArgumentClass(
@@ -155,10 +161,22 @@
syntheticNestStaticMethodAccessorMethodSubject,
notIf(isPresent(), parameters.canUseNestBasedAccesses()));
- // TODO(b/265729283): Should contain the nest bridge methods and the synthesized constructor
- // argument class.
+ // Verify the residual profile contains the synthetic nest based access bridges and the
+ // synthetic constructor argument class.
profileInspector
.assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .applyIf(
+ !parameters.canUseNestBasedAccesses(),
+ i ->
+ i.assertContainsMethodRules(
+ instanceInitializerWithSyntheticArgumentSubject,
+ syntheticNestInstanceFieldGetterMethodSubject,
+ syntheticNestInstanceFieldSetterMethodSubject,
+ syntheticNestInstanceMethodAccessorMethodSubject,
+ syntheticNestStaticFieldGetterMethodSubject,
+ syntheticNestStaticFieldSetterMethodSubject,
+ syntheticNestStaticMethodAccessorMethodSubject)
+ .assertContainsClassRule(syntheticConstructorArgumentClassSubject))
.assertContainsNoOtherRules();
}