blob: c82116a52a08dead45ce20f46ca0eea27347cb42 [file] [log] [blame]
// Copyright (c) 2024, 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.graph;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public class ReferencedMembersCollector {
public interface ReferencedMembersConsumer {
void onFieldReference(DexField field, ProgramMethod context);
void onMethodReference(DexMethod method, ProgramMethod context);
}
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final ReferencedMembersConsumer consumer;
public ReferencedMembersCollector(
AppView<? extends AppInfoWithClassHierarchy> appView, ReferencedMembersConsumer consumer) {
this.appView = appView;
this.consumer = consumer;
}
public void run(ExecutorService executorService) throws ExecutionException {
ThreadUtils.processItems(
appView.appInfo().classes(),
this::processClass,
appView.options().getThreadingModule(),
executorService);
}
private void processClass(DexProgramClass clazz) {
clazz.forEachProgramMethodMatching(DexEncodedMethod::hasCode, this::processMethod);
}
private void processMethod(ProgramMethod method) {
Code code = method.getDefinition().getCode();
if (code.isCfCode()) {
assert appView.isCfByteCodePassThrough(method.getDefinition());
processCfCode(method, code.asCfCode());
} else if (code.isDefaultInstanceInitializerCode()) {
processDefaultInstanceInitializerCode(method);
} else if (code.isLirCode()) {
processLirCode(method, code.asLirCode());
} else if (code.isThrowNullCode()) {
// Intentionally empty.
} else {
assert false : code.getClass().getTypeName();
}
}
private void processDefaultInstanceInitializerCode(ProgramMethod method) {
DexMethod invokedMethod =
DefaultInstanceInitializerCode.getParentConstructor(method, appView.dexItemFactory());
consumer.onMethodReference(invokedMethod, method);
}
private void processCfCode(ProgramMethod method, CfCode code) {
for (CfInstruction instruction : code.getInstructions()) {
if (instruction.isFieldInstruction()) {
consumer.onFieldReference(instruction.asFieldInstruction().getField(), method);
} else if (instruction.isInvoke()) {
consumer.onMethodReference(instruction.asInvoke().getMethod(), method);
} else if (instruction.isInvokeDynamic()) {
processCallSite(method, instruction.asInvokeDynamic().getCallSite());
}
}
}
private void processLirCode(ProgramMethod method, LirCode<Integer> code) {
for (LirConstant constant : code.getConstantPool()) {
if (constant instanceof DexField) {
consumer.onFieldReference((DexField) constant, method);
} else if (constant instanceof DexCallSite) {
processCallSite(method, (DexCallSite) constant);
} else if (constant instanceof DexMethod) {
consumer.onMethodReference((DexMethod) constant, method);
} else if (constant instanceof DexMethodHandle) {
processMethodHandle(method, (DexMethodHandle) constant);
}
}
}
private void processCallSite(ProgramMethod method, DexCallSite callSite) {
processMethodHandle(method, callSite.getBootstrapMethod());
for (DexValue bootstrapArg : callSite.getBootstrapArgs()) {
if (bootstrapArg.isDexValueField()) {
consumer.onFieldReference(bootstrapArg.asDexValueField().getValue(), method);
} else if (bootstrapArg.isDexValueMethod()) {
consumer.onMethodReference(bootstrapArg.asDexValueMethod().getValue(), method);
} else if (bootstrapArg.isDexValueMethodHandle()) {
processMethodHandle(method, bootstrapArg.asDexValueMethodHandle().getValue());
}
}
}
private void processMethodHandle(ProgramMethod method, DexMethodHandle methodHandle) {
if (methodHandle.isFieldHandle()) {
consumer.onFieldReference(methodHandle.asField(), method);
} else {
assert methodHandle.isMethodHandle();
consumer.onMethodReference(methodHandle.asMethod(), method);
}
}
}