blob: 738f4aa9998fbaeddd5f1e214db265ad4fa682d5 [file] [log] [blame]
// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
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.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.BasicBlock;
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 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;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
// 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 D8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
// Maps a nest host to a class met which has that nest host.
// The value is used because the nest host might be missing.
private final Map<DexType, DexClass> metNestHosts = new ConcurrentHashMap<>();
public D8NestBasedAccessDesugaring(AppView<?> appView) {
super(appView);
}
public void rewriteNestBasedAccesses(
DexEncodedMethod encodedMethod, IRCode code, AppView<?> appView) {
DexClass currentClass = appView.definitionFor(encodedMethod.holder());
assert currentClass != null;
if (!currentClass.isInANest()) {
return;
}
metNestHosts.put(currentClass.getNestHost(), currentClass);
ListIterator<BasicBlock> blocks = code.listIterator();
while (blocks.hasNext()) {
BasicBlock block = blocks.next();
InstructionListIterator instructions = block.listIterator(code);
while (instructions.hasNext()) {
Instruction instruction = instructions.next();
if (instruction.isInvokeMethod()) {
InvokeMethod invokeMethod = instruction.asInvokeMethod();
DexMethod methodCalled = invokeMethod.getInvokedMethod();
DexEncodedMethod encodedMethodCalled =
methodCalled.holder.isClassType() ? appView.definitionFor(methodCalled) : null;
if (encodedMethodCalled != null
&& invokeRequiresRewriting(encodedMethodCalled, currentClass)) {
DexMethod bridge = ensureInvokeBridge(encodedMethodCalled);
if (encodedMethodCalled.isInstanceInitializer()) {
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(bridge, invokeMethod.outValue(), parameters));
} else {
instructions.replaceCurrentInstruction(
new InvokeStatic(bridge, invokeMethod.outValue(), invokeMethod.arguments()));
}
}
} else if (instruction.isFieldInstruction()) {
DexEncodedField encodedField =
appView.definitionFor(instruction.asFieldInstruction().getField());
if (encodedField != null && fieldAccessRequiresRewriting(encodedField, currentClass)) {
if (instruction.isInstanceGet() || instruction.isStaticGet()) {
DexMethod bridge = ensureFieldAccessBridge(encodedField, true);
instructions.replaceCurrentInstruction(
new InvokeStatic(bridge, instruction.outValue(), instruction.inValues()));
} else {
assert instruction.isInstancePut() || instruction.isStaticPut();
DexMethod bridge = ensureFieldAccessBridge(encodedField, false);
instructions.replaceCurrentInstruction(
new InvokeStatic(bridge, instruction.outValue(), instruction.inValues()));
}
}
}
}
}
}
private void processNestsConcurrently(ExecutorService executorService) throws ExecutionException {
List<Future<?>> futures = new ArrayList<>();
for (DexClass clazz : metNestHosts.values()) {
futures.add(asyncProcessNest(clazz, executorService));
}
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.holder());
assert holder != null && holder.isProgramClass();
holder.asProgramClass().addMethod(bridge);
}
}
private void optimizeDeferredBridgesConcurrently(
ExecutorService executorService, IRConverter converter) throws ExecutionException {
Collection<DexEncodedMethod> methods = new ArrayList<>();
methods.addAll(bridges.values());
methods.addAll(getFieldBridges.values());
methods.addAll(putFieldBridges.values());
converter.processMethodsConcurrently(methods, executorService);
}
public void desugarNestBasedAccess(
DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
throws ExecutionException {
processNestsConcurrently(executorService);
addDeferredBridges();
synthesizeNestConstructor(builder);
optimizeDeferredBridgesConcurrently(executorService, converter);
}
// 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();
}
@Override
void reportMissingNestHost(DexClass clazz) {
appView.options().nestDesugaringWarningMissingNestHost(clazz);
}
@Override
void reportIncompleteNest(List<DexType> nest) {
appView.options().nestDesugaringWarningIncompleteNest(nest, appView);
}
}