blob: d4e25df6dd799c3033674fb5454e3fd7602638ed [file] [log] [blame]
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;
import com.android.tools.r8.ir.code.InvokeDirect;
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.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 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);
}
@Override
protected void shouldRewriteCalls(DexMethod method, DexMethod bridge) {
methodMap.put(method, bridge);
}
@Override
protected void shouldRewriteInitializers(DexMethod method, DexMethod bridge) {
initializerMap.put(method, bridge);
}
@Override
protected void shouldRewriteStaticGetFields(DexField field, DexMethod bridge) {
staticGetToMethodMap.put(field, bridge);
}
@Override
protected void shouldRewriteStaticPutFields(DexField field, DexMethod bridge) {
staticPutToMethodMap.put(field, bridge);
}
@Override
protected void shouldRewriteInstanceGetFields(DexField field, DexMethod bridge) {
instanceGetToMethodMap.put(field, bridge);
}
@Override
protected void shouldRewriteInstancePutFields(DexField field, DexMethod bridge) {
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(
FieldInstruction fieldInstruction,
InstructionListIterator instructions,
DexMethod method,
Map<DexField, DexMethod> fieldToMethodMap) {
DexMethod newTarget = fieldToMethodMap.get(fieldInstruction.getField());
if (newTarget != null && method != newTarget) {
instructions.replaceCurrentInstruction(
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();
InstructionListIterator instructions = block.listIterator();
while (instructions.hasNext()) {
Instruction instruction = instructions.next();
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(
new InvokeStatic(newTarget, invokeMethod.outValue(), invokeMethod.arguments()));
} else {
newTarget = initializerMap.get(methodCalled);
if (newTarget != null && encodedMethod.method != newTarget) {
// insert extra null value and replace call.
instructions.previous();
Value extraNullValue =
instructions.insertConstNullInstruction(code, appView.options());
instructions.next();
List<Value> parameters = new ArrayList<>(invokeMethod.arguments());
parameters.add(extraNullValue);
instructions.replaceCurrentInstruction(
new InvokeDirect(newTarget, invokeMethod.outValue(), parameters));
}
}
} 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();
}
}