blob: b83536214920eb9c829cf33bf876736ae6f15df1 [file]
// Copyright (c) 2026, 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.tracereferences;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.origin.MethodOrigin;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public class NativeReferencesHelper {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final TraceReferencesNativeReferencesConsumer nativeReferencesConsumer;
private final DiagnosticsHandler diagnostics;
private final DexItemFactory factory;
private final DexString loadLibrary;
private final DexString load;
public NativeReferencesHelper(
AppView<? extends AppInfoWithClassHierarchy> appView,
TraceReferencesNativeReferencesConsumer nativeReferencesConsumer,
DiagnosticsHandler diagnostics) {
this.appView = appView;
this.nativeReferencesConsumer = nativeReferencesConsumer;
this.diagnostics = diagnostics;
this.factory = appView.dexItemFactory();
this.loadLibrary = factory.createString("loadLibrary");
this.load = factory.createString("load");
}
public boolean isSystemLoadLibrary(DexMethod method) {
return method.getHolderType().isIdenticalTo(factory.javaLangSystemType)
&& method.getName().isIdenticalTo(loadLibrary);
}
public boolean isSystemLoad(DexMethod method) {
return method.getHolderType().isIdenticalTo(factory.javaLangSystemType)
&& method.getName().isIdenticalTo(load);
}
public void process(Collection<DexProgramClass> classes, ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
classes,
clazz ->
clazz.forEachProgramMethodMatching(
method -> method.isNative() || methodMightCallSystemLoadOrSystemLoadLibrary(method),
this::processMethod),
appView.options().getThreadingModule(),
executorService);
}
public void process(ProgramMethodSet methods, ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
methods, this::processMethod, appView.options().getThreadingModule(), executorService);
}
private void processMethod(ProgramMethod method) {
if (method.getAccessFlags().isNative()) {
nativeReferencesConsumer.acceptNativeMethod(
appView
.getNamingLens()
.lookupMethod(method.getReference(), appView.dexItemFactory())
.asMethodReference(),
diagnostics);
return;
}
IRCode code = method.buildIR(appView, MethodConversionOptions.nonConverting());
for (InvokeStatic invoke : code.<InvokeStatic>instructions(Instruction::isInvokeStatic)) {
processInvoke(method, invoke);
}
}
private void processInvoke(ProgramMethod method, InvokeStatic invoke) {
DexMethod invokedMethod = invoke.getInvokedMethod();
if (isSystemLoadLibrary(invokedMethod) || isSystemLoad(invokedMethod)) {
Value argument = invoke.getFirstArgument().getAliasedValue();
MethodOrigin origin =
new MethodOrigin(
appView
.getNamingLens()
.lookupMethod(method.getReference(), appView.dexItemFactory())
.asMethodReference(),
method.getOrigin());
if (argument.isConstString()) {
String name = argument.getDefinition().asConstString().getValue().toString();
if (isSystemLoadLibrary(invokedMethod)) {
nativeReferencesConsumer.acceptLoadLibrary(name, origin, diagnostics);
} else {
nativeReferencesConsumer.acceptLoad(name, origin, diagnostics);
}
} else {
if (isSystemLoadLibrary(invokedMethod)) {
nativeReferencesConsumer.acceptLoadLibraryAny(origin, diagnostics);
} else {
nativeReferencesConsumer.acceptLoadAny(origin, diagnostics);
}
}
}
}
private boolean methodMightCallSystemLoadOrSystemLoadLibrary(DexEncodedMethod method) {
if (!method.hasCode()) {
return false;
}
if (!method.getCode().isLirCode()) {
return true;
}
for (LirConstant constant : method.getCode().asLirCode().getConstantPool()) {
if (constant instanceof DexMethod) {
DexMethod methodConstant = (DexMethod) constant;
if (isSystemLoadLibrary(methodConstant) || isSystemLoad(methodConstant)) {
return true;
}
}
}
return false;
}
}