blob: aa3f5170521f560e960208b0d5762aae505782a0 [file]
// 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.assistant.runtime;
import com.android.tools.r8.assistant.runtime.ReflectiveOperationReceiver.ClassFlag;
import com.android.tools.r8.assistant.runtime.ReflectiveOperationReceiver.NameLookupType;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
@KeepForApi
public class ReflectiveOracle {
private static Object instanceLock = new Object();
private static volatile ReflectiveOperationReceiver INSTANCE;
private static ReflectiveOperationReceiver getInstance() {
if (INSTANCE == null) {
// TODO(b/393249304): Support injecting alternative receiver.
synchronized (instanceLock) {
if (INSTANCE == null) {
INSTANCE = getReceiver();
}
}
}
return INSTANCE;
}
// Might be rewritten to call new instance on a custom receiver.
private static ReflectiveOperationReceiver getReceiver() {
// Default, might be replaced, don't change this without changing the instrumentation
return new ReflectiveOperationJsonLogger();
}
@KeepForApi
public static class Stack {
private final StackTraceElement[] stackTraceElements;
private Stack(StackTraceElement[] stackTraceElements) {
this.stackTraceElements = stackTraceElements;
}
static Stack createStack() {
assert INSTANCE != null;
if (INSTANCE.requiresStackInformation()) {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
return new Stack(Arrays.copyOfRange(stackTrace, 2, stackTrace.length));
}
return new Stack(null);
}
public StackTraceElement[] getStackTraceElements() {
return stackTraceElements;
}
public String toStringStackTrace(int numberOfLevels) {
if (stackTraceElements == null) {
return "Stack extraction not enabled.";
}
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : stackTraceElements) {
sb.append(" at ").append(element).append("\n");
if (numberOfLevels == 0) {
break;
}
numberOfLevels--;
}
return sb.toString();
}
public String[] stackTraceElementsAsString() {
String[] result = new String[stackTraceElements.length];
for (int i = 0; i < stackTraceElements.length; i++) {
result[i] = stackTraceElements[i].toString();
}
return result;
}
}
public static void onClassNewInstance(Class<?> clazz) {
getInstance().onClassNewInstance(Stack.createStack(), clazz);
}
public static void onClassForNameDefault(String className) {
// The last parameter is implicitly the caller's holder class class loader.
getInstance().onClassForName(Stack.createStack(), className, true, null);
}
public static void onClassForNameWithLoader(
String className, boolean initialize, ClassLoader classLoader) {
getInstance().onClassForName(Stack.createStack(), className, initialize, classLoader);
}
public static void onClassGetDeclaredMethod(Class<?> clazz, String name, Class<?>... parameters) {
Class<?> returnType = null;
try {
Method declaredMethod = clazz.getDeclaredMethod(name, parameters);
returnType = declaredMethod.getReturnType();
} catch (NoSuchMethodException e) {
}
getInstance()
.onClassGetDeclaredMethod(Stack.createStack(), returnType, clazz, name, parameters);
}
public static void onClassGetDeclaredMethods(Class<?> clazz) {
getInstance().onClassGetDeclaredMethods(Stack.createStack(), clazz);
}
public static void onClassGetDeclaredField(Class<?> clazz, String fieldName) {
Class<?> fieldType = null;
try {
fieldType = clazz.getDeclaredField(fieldName).getType();
} catch (NoSuchFieldException e) {
}
getInstance().onClassGetDeclaredField(Stack.createStack(), fieldType, clazz, fieldName);
}
public static void onClassGetDeclaredFields(Class<?> clazz) {
getInstance().onClassGetDeclaredFields(Stack.createStack(), clazz);
}
public static void onClassGetDeclaredConstructor(Class<?> clazz, Class<?>... parameters) {
getInstance().onClassGetDeclaredConstructor(Stack.createStack(), clazz, parameters);
}
public static void onClassGetDeclaredConstructors(Class<?> clazz) {
getInstance().onClassGetDeclaredConstructors(Stack.createStack(), clazz);
}
public static void onClassGetMethod(Class<?> clazz, String name, Class<?>[] parameterTypes) {
Class<?> returnType = null;
try {
returnType = clazz.getMethod(name, parameterTypes).getReturnType();
} catch (NoSuchMethodException e) {
}
getInstance().onClassGetMethod(Stack.createStack(), returnType, clazz, name, parameterTypes);
}
public static void onClassGetMethods(Class<?> clazz) {
getInstance().onClassGetMethods(Stack.createStack(), clazz);
}
public static void onClassGetField(Class<?> clazz, String name) {
Class<?> fieldType = null;
try {
fieldType = clazz.getField(name).getType();
} catch (NoSuchFieldException e) {
}
getInstance().onClassGetField(Stack.createStack(), fieldType, clazz, name);
}
public static void onClassGetFields(Class<?> clazz) {
getInstance().onClassGetFields(Stack.createStack(), clazz);
}
public static void onClassGetConstructor(Class<?> clazz, Class<?>... parameterTypes) {
getInstance().onClassGetConstructor(Stack.createStack(), clazz, parameterTypes);
}
public static void onClassGetConstructors(Class<?> clazz) {
getInstance().onClassGetConstructors(Stack.createStack(), clazz);
}
public static void onClassGetName(Class<?> clazz) {
getInstance().onClassGetName(Stack.createStack(), clazz, NameLookupType.NAME);
}
public static void onClassGetSimpleName(Class<?> clazz) {
getInstance().onClassGetName(Stack.createStack(), clazz, NameLookupType.SIMPLE_NAME);
}
public static void onClassGetCanonicalName(Class<?> clazz) {
getInstance().onClassGetName(Stack.createStack(), clazz, NameLookupType.CANONICAL_NAME);
}
public static void onClassGetTypeName(Class<?> clazz) {
getInstance().onClassGetName(Stack.createStack(), clazz, NameLookupType.TYPE_NAME);
}
public static void onClassGetSuperclass(Class<?> clazz) {
getInstance().onClassGetSuperclass(Stack.createStack(), clazz);
}
public static void onClassAsSubclass(Class<?> holder, Class<?> clazz) {
getInstance().onClassAsSubclass(Stack.createStack(), holder, clazz);
}
public static void onClassIsInstance(Class<?> holder, Object object) {
getInstance().onClassIsInstance(Stack.createStack(), holder, object);
}
public static void onClassCast(Class<?> holder, Object object) {
getInstance().onClassCast(Stack.createStack(), holder, object);
}
public static void onClassGetComponentType(Class<?> clazz) {
getInstance().onClassGetComponentType(Stack.createStack(), clazz);
}
public static void onClassGetPackage(Class<?> clazz) {
getInstance().onClassGetPackage(Stack.createStack(), clazz);
}
public static void onClassIsAssignableFrom(Class<?> clazz, Class<?> sup) {
getInstance().onClassIsAssignableFrom(Stack.createStack(), clazz, sup);
}
public static void onClassIsAnnotation(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.ANNOTATION);
}
public static void onClassIsAnonymousClass(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.ANONYMOUS_CLASS);
}
public static void onClassIsArray(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.ARRAY);
}
public static void onClassIsEnum(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.ENUM);
}
public static void onClassIsHidden(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.HIDDEN);
}
public static void onClassIsInterface(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.INTERFACE);
}
public static void onClassIsLocalClass(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.LOCAL_CLASS);
}
public static void onClassIsMemberClass(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.MEMBER_CLASS);
}
public static void onClassIsPrimitive(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.PRIMITIVE);
}
public static void onClassIsRecord(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.RECORD);
}
public static void onClassIsSealed(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.SEALED);
}
public static void onClassIsSynthetic(Class<?> clazz) {
getInstance().onClassFlag(Stack.createStack(), clazz, ClassFlag.SYNTHETIC);
}
public static void onAtomicIntegerFieldUpdaterNewUpdater(Class<?> clazz, String name) {
getInstance().onAtomicFieldUpdaterNewUpdater(Stack.createStack(), int.class, clazz, name);
}
public static void onAtomicLongFieldUpdaterNewUpdater(Class<?> clazz, String name) {
getInstance().onAtomicFieldUpdaterNewUpdater(Stack.createStack(), long.class, clazz, name);
}
public static void onAtomicReferenceFieldUpdaterNewUpdater(
Class<?> clazz, Class<?> fieldClass, String name) {
getInstance().onAtomicFieldUpdaterNewUpdater(Stack.createStack(), fieldClass, clazz, name);
}
public static void onServiceLoaderLoad(Class<?> clazz) {
getInstance().onServiceLoaderLoad(Stack.createStack(), clazz, null);
}
public static void onServiceLoaderLoadWithClassLoader(Class<?> clazz, ClassLoader classLoader) {
getInstance().onServiceLoaderLoad(Stack.createStack(), clazz, classLoader);
}
public static void onServiceLoaderLoadInstalled(Class<?> clazz) {
getInstance()
.onServiceLoaderLoad(Stack.createStack(), clazz, ClassLoader.getSystemClassLoader());
}
public static void onProxyNewProxyInstance(
ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler invocationHandler) {
getInstance()
.onProxyNewProxyInstance(Stack.createStack(), classLoader, interfaces, invocationHandler);
}
}