blob: 9deb73336dcf2810d027d720d523b66285fb2854 [file] [log] [blame]
// Copyright (c) 2025, 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.partial;
import com.android.tools.r8.cf.code.CfFieldInstruction;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.dex.code.DexIgetOrIputOrSgetOrSput;
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.dex.code.DexInvokeMethodOrInvokeMethodRange;
import com.android.tools.r8.dex.code.DexNewInstance;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexCode;
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.ProgramMethod;
import com.android.tools.r8.graph.ThrowExceptionCode;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.MethodLookupResult;
import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.ir.conversion.LirConverter;
import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialD8SubCompilationConfiguration;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.timing.Timing;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public class R8PartialApplicationWriter {
private final AppView<AppInfo> appView;
private final InternalOptions options;
private final R8PartialD8SubCompilationConfiguration subCompilationConfiguration;
public R8PartialApplicationWriter(AppView<AppInfo> appView) {
this.appView = appView;
this.options = appView.options();
this.subCompilationConfiguration = appView.options().partialSubCompilationConfiguration.asD8();
}
public void write(ExecutorService executorService) throws ExecutionException {
assert appView.getNamingLens().isIdentityLens();
// We need to rewrite the code with the lenses here, since the D8 lenses are normally applied
// during writing, which we bypass. The D8 lenses may arise from synthetic finalization and
// horizontal class merging of synthetics.
rewriteCodeWithLens(executorService);
subCompilationConfiguration.writeApplication(appView);
}
private void rewriteCodeWithLens(ExecutorService executorService) throws ExecutionException {
if (appView.graphLens() == appView.codeLens()) {
assert appView.graphLens().isIdentityLens();
return;
}
ThreadUtils.processItems(
appView.appInfo().classes(),
this::rewriteCodeWithLens,
options.getThreadingModule(),
executorService);
}
private void rewriteCodeWithLens(DexProgramClass clazz) {
clazz.forEachProgramMethod(this::rewriteCodeWithLens);
}
private void rewriteCodeWithLens(ProgramMethod method) {
if (!method.getDefinition().hasCode()) {
return;
}
Code code = method.getDefinition().getCode();
if (code.isCfCode()) {
rewriteCfCodeWithLens(code.asCfCode(), method);
} else if (code.isDexCode()) {
rewriteDexCodeWithLens(code.asDexCode(), method);
} else if (code.isLirCode()) {
LirConverter.rewriteLirMethodWithLens(
method, appView, appView.graphLens(), LensCodeRewriterUtils.empty(), Timing.empty());
} else if (code.isThrowExceptionCode()) {
assert verifyThrowExceptionCodeIsUpToDate(method, code.asThrowExceptionCode());
} else {
assert false;
}
}
private void rewriteCfCodeWithLens(CfCode code, ProgramMethod method) {
ListUtils.map(
code.getInstructions(),
i -> rewriteCfInstructionWithLens(i, method),
code::setInstructions);
}
private CfInstruction rewriteCfInstructionWithLens(
CfInstruction instruction, ProgramMethod context) {
if (instruction.isInvoke()) {
CfInvoke invoke = instruction.asInvoke();
DexMethod invokedMethod = invoke.getMethod();
MethodLookupResult lookupResult =
appView
.graphLens()
.lookupMethod(
invokedMethod,
context.getReference(),
invoke.getInvokeType(appView, appView.codeLens(), context),
appView.codeLens());
DexMethod rewrittenInvokedMethod = lookupResult.getReference();
if (rewrittenInvokedMethod.isNotIdenticalTo(invokedMethod)) {
return invoke.withMethod(rewrittenInvokedMethod, invoke.isInterface());
}
}
if (instruction.isNew()) {
CfNew cfNew = instruction.asNew();
DexType type = cfNew.getType();
DexType rewrittenType = appView.graphLens().lookupType(type, appView.codeLens());
if (rewrittenType.isNotIdenticalTo(type)) {
return cfNew.withType(rewrittenType);
}
} else if (instruction.isFieldInstruction()) {
CfFieldInstruction fieldInstruction = instruction.asFieldInstruction();
DexField field = fieldInstruction.getField();
DexField rewrittenField = appView.graphLens().lookupField(field, appView.codeLens());
if (rewrittenField.isNotIdenticalTo(field)) {
return fieldInstruction.createWithField(rewrittenField);
}
}
return instruction;
}
private void rewriteDexCodeWithLens(DexCode code, ProgramMethod method) {
ArrayUtils.map(
code.getInstructions(),
i -> rewriteDexInstructionWithLens(i, method),
DexInstruction.EMPTY_ARRAY,
newInstructions -> method.setCode(code.withNewInstructions(newInstructions), appView));
}
private DexInstruction rewriteDexInstructionWithLens(
DexInstruction instruction, ProgramMethod context) {
if (instruction.isInvokeMethodOrInvokeMethodRange()) {
DexInvokeMethodOrInvokeMethodRange invoke = instruction.asInvokeMethodOrInvokeMethodRange();
DexMethod invokedMethod = invoke.getMethod();
MethodLookupResult lookupResult =
appView
.graphLens()
.lookupMethod(
invokedMethod, context.getReference(), invoke.getType(), appView.codeLens());
DexMethod rewrittenInvokedMethod = lookupResult.getReference();
if (rewrittenInvokedMethod.isNotIdenticalTo(invokedMethod)) {
return invoke.withMethod(rewrittenInvokedMethod).asDexInstruction();
}
} else if (instruction.isNewInstance()) {
DexNewInstance newInstance = instruction.asNewInstance();
DexType type = newInstance.getType();
DexType rewrittenType = appView.graphLens().lookupType(type, appView.codeLens());
if (rewrittenType.isNotIdenticalTo(type)) {
return newInstance.withType(rewrittenType);
}
} else if (instruction.isIgetOrIputOrSgetOrSput()) {
DexIgetOrIputOrSgetOrSput fieldInstruction = instruction.asIgetOrIputOrSgetOrSput();
DexField field = fieldInstruction.getField();
DexField rewrittenField = appView.graphLens().lookupField(field, appView.codeLens());
if (rewrittenField.isNotIdenticalTo(field)) {
return fieldInstruction.withField(rewrittenField).asDexInstruction();
}
}
return instruction;
}
private boolean verifyThrowExceptionCodeIsUpToDate(
ProgramMethod method, ThrowExceptionCode code) {
GraphLens graphLens = appView.graphLens();
GraphLens codeLens = appView.codeLens();
DexType exceptionType = code.asThrowExceptionCode().getExceptionType();
DexMethod exceptionConstructor =
appView.dexItemFactory().createInstanceInitializer(exceptionType);
assert exceptionType.isIdenticalTo(graphLens.lookupType(exceptionType, codeLens));
assert exceptionConstructor.isIdenticalTo(
graphLens
.lookupMethod(
exceptionConstructor, method.getReference(), InvokeType.DIRECT, codeLens, false)
.getReference());
return true;
}
}