| // 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; | |
| } | |
| } |