All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.svm.agent.BreakpointInterceptor Maven / Gradle / Ivy

There is a newer version: 22.0.0.2
Show newest version
/*
 * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.svm.agent;

import static com.oracle.svm.core.util.VMError.guarantee;
import static com.oracle.svm.jni.JNIObjectHandles.nullHandle;
import static com.oracle.svm.jvmtiagentbase.Support.callObjectMethod;
import static com.oracle.svm.jvmtiagentbase.Support.check;
import static com.oracle.svm.jvmtiagentbase.Support.checkNoException;
import static com.oracle.svm.jvmtiagentbase.Support.clearException;
import static com.oracle.svm.jvmtiagentbase.Support.fromCString;
import static com.oracle.svm.jvmtiagentbase.Support.fromJniString;
import static com.oracle.svm.jvmtiagentbase.Support.getClassNameOr;
import static com.oracle.svm.jvmtiagentbase.Support.getClassNameOrNull;
import static com.oracle.svm.jvmtiagentbase.Support.getMethodDeclaringClass;
import static com.oracle.svm.jvmtiagentbase.Support.getObjectArgument;
import static com.oracle.svm.jvmtiagentbase.Support.handleException;
import static com.oracle.svm.jvmtiagentbase.Support.jniFunctions;
import static com.oracle.svm.jvmtiagentbase.Support.jvmtiEnv;
import static com.oracle.svm.jvmtiagentbase.Support.jvmtiFunctions;
import static com.oracle.svm.jvmtiagentbase.Support.newObjectL;
import static com.oracle.svm.jvmtiagentbase.Support.testException;
import static com.oracle.svm.jvmtiagentbase.Support.toCString;
import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_BREAKPOINT;
import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_CLASS_FILE_LOAD_HOOK;
import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_CLASS_PREPARE;
import static org.graalvm.word.WordFactory.nullPointer;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;

import org.graalvm.compiler.core.common.NumUtil;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.UnmanagedMemory;
import org.graalvm.nativeimage.c.function.CEntryPoint;
import org.graalvm.nativeimage.c.function.CEntryPointLiteral;
import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CCharPointerPointer;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;
import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder;
import org.graalvm.nativeimage.c.type.WordPointer;

import com.oracle.svm.agent.stackaccess.InterceptedState;
import com.oracle.svm.agent.tracing.core.Tracer;
import com.oracle.svm.configure.trace.AccessAdvisor;
import com.oracle.svm.core.c.function.CEntryPointOptions;
import com.oracle.svm.core.jdk.Package_jdk_internal_reflect;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.jni.JNIObjectHandles;
import com.oracle.svm.jni.nativeapi.JNIEnvironment;
import com.oracle.svm.jni.nativeapi.JNIFieldId;
import com.oracle.svm.jni.nativeapi.JNIMethodId;
import com.oracle.svm.jni.nativeapi.JNIObjectHandle;
import com.oracle.svm.jni.nativeapi.JNIValue;
import com.oracle.svm.jvmtiagentbase.AgentIsolate;
import com.oracle.svm.jvmtiagentbase.ConstantPoolTool;
import com.oracle.svm.jvmtiagentbase.ConstantPoolTool.MethodReference;
import com.oracle.svm.jvmtiagentbase.Support;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiCapabilities;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEnv;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiError;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEventCallbacks;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEventMode;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiFrameInfo;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiLocationFormat;

/**
 * Intercepts events of interest via breakpoints in Java code.
 * 

* With most of our breakpoints, we recursively call the intercepted method ourselves to inspect its * return value and determine whether it provides a valid result. This permits us to identify * probing. *

* Some of the methods are caller-sensitive, so when we call them from a breakpoint, they observe a * different caller class and therefore can behave differently. Short of using bytecode * instrumentation to read the return value, there seems to be no strictly better approach (and * instrumenting java.lang.Class and friends might be tricky, too). It would be possible to set * breakpoints at return bytecodes instead, but then there is no way to retrieve the return value * from the operand stack. *

* When other tools or code use bytecode reinstrumentation or code hotswapping on a method in which * we already have a breakpoint, our breakpoint is cleared. It seems that we cannot get a useful * event in that case: according to the JVMTI specification, only ClassFileLoadHook triggers, but at * a time when the changes are not yet applied and it is too early to set another breakpoint. * Therefore, we do not support this case for now. */ final class BreakpointInterceptor { private static Tracer tracer; private static NativeImageAgent agent; private static Supplier interceptedStateSupplier; private static Map installedBreakpoints; /* * NOTE: support for breakpoints in native methods has been removed. * * Restore from version control if needed. */ /** Enables experimental support for instrumenting class lookups via {@code ClassLoader}. */ private static boolean experimentalClassLoaderSupport = false; /** Enables experimental support for class definitions via {@code ClassLoader.defineClass}. */ private static boolean experimentalClassDefineSupport = false; /** Enables tracking of reflection queries for fine-tuned configuration. */ private static boolean trackReflectionMetadata = false; /** * Locations in methods where explicit calls to {@code ClassLoader.loadClass} have been found. */ private static ConcurrentMap observedExplicitLoadClassCallSites; private static final ThreadLocal recursive = ThreadLocal.withInitial(() -> Boolean.FALSE); /* Classes from these class loaders are assumed to not be dynamically loaded. */ private static JNIObjectHandle[] builtinClassLoaders; private static void traceBreakpoint(JNIEnvironment env, JNIObjectHandle clazz, JNIObjectHandle declaringClass, JNIObjectHandle callerClass, String function, Object result, JNIMethodId[] stackTrace, Object... args) { if (tracer != null) { tracer.traceCall("reflect", function, getClassNameOr(env, clazz, null, Tracer.UNKNOWN_VALUE), getClassNameOr(env, declaringClass, null, Tracer.UNKNOWN_VALUE), getClassNameOr(env, callerClass, null, Tracer.UNKNOWN_VALUE), result, stackTrace, args); JNIObjectHandle exception = handleException(env, false); if (exception.notEqual(nullHandle())) { /* * A stack overflow error happening during a breakpoint should be handled by the * program, not the agent. */ guarantee(jniFunctions().getIsInstanceOf().invoke(env, exception, agent.handles().javaLangStackOverflowError)); return; } clearException(env); } } private static boolean forName(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle name = getObjectArgument(0); String className = fromJniString(jni, name); boolean classLoaderValid = true; WordPointer classLoaderPtr = StackValue.get(WordPointer.class); if (bp.method == agent.handles().javaLangClassForName3) { classLoaderValid = (jvmtiFunctions().GetLocalObject().invoke(jvmtiEnv(), nullHandle(), 0, 2, classLoaderPtr) == JvmtiError.JVMTI_ERROR_NONE); } else { classLoaderPtr.write(nullHandle()); if (callerClass.notEqual(nullHandle())) { /* * NOTE: we use our direct caller class, but this class might be skipped over by * Class.forName(nameOnly) in its security stackwalk for @CallerSensitive, leading * to different behavior of our call and the original call. */ classLoaderValid = (jvmtiFunctions().GetClassLoader().invoke(jvmtiEnv(), callerClass, classLoaderPtr) == JvmtiError.JVMTI_ERROR_NONE); } } Object result = Tracer.UNKNOWN_VALUE; if (classLoaderValid) { /* * Even if the original call requested class initialization, disable it because * recursion checks keep us from seeing events of interest during initialization. */ int initialize = 0; JNIObjectHandle loadedClass = Support.callStaticObjectMethodLIL(jni, bp.clazz, agent.handles().javaLangClassForName3, name, initialize, classLoaderPtr.read()); if (clearException(jni)) { loadedClass = nullHandle(); } result = loadedClass.notEqual(nullHandle()); } traceBreakpoint(jni, bp.clazz, nullHandle(), callerClass, bp.specification.methodName, result, state.getFullStackTraceOrNull(), className); return true; } private static boolean getFields(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetFields(jni, bp, state); } private static boolean getDeclaredFields(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetFields(jni, bp, state); } private static boolean handleGetFields(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); traceBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, null, state.getFullStackTraceOrNull()); return true; } private static boolean getMethods(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetMethods(jni, bp, state); } private static boolean getDeclaredMethods(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetMethods(jni, bp, state); } private static boolean getConstructors(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetMethods(jni, bp, state); } private static boolean getDeclaredConstructors(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetMethods(jni, bp, state); } private static boolean handleGetMethods(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); traceBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, null, state.getFullStackTraceOrNull()); return true; } private static boolean getClasses(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetClasses(jni, bp, state); } private static boolean getDeclaredClasses(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetClasses(jni, bp, state); } private static boolean handleGetClasses(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); traceBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, null, state.getFullStackTraceOrNull()); return true; } private static boolean getField(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetField(jni, bp, false, state); } private static boolean getDeclaredField(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetField(jni, bp, true, state); } private static boolean handleGetField(JNIEnvironment jni, Breakpoint bp, boolean declaredOnly, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); JNIObjectHandle name = getObjectArgument(1); JNIObjectHandle result = Support.callObjectMethodL(jni, self, bp.method, name); if (clearException(jni)) { result = nullHandle(); } JNIObjectHandle declaring = nullHandle(); if (!declaredOnly && result.notEqual(nullHandle())) { declaring = Support.callObjectMethod(jni, result, agent.handles().javaLangReflectMemberGetDeclaringClass); if (clearException(jni)) { declaring = nullHandle(); } } traceBreakpoint(jni, self, declaring, callerClass, bp.specification.methodName, result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), fromJniString(jni, name)); return true; } private static boolean objectFieldOffsetByName(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); JNIObjectHandle declaring = getObjectArgument(1); JNIObjectHandle name = getObjectArgument(2); Support.callLongMethodLL(jni, self, bp.method, declaring, name); boolean validResult = !clearException(jni); JNIObjectHandle clazz = getMethodDeclaringClass(bp.method); traceBreakpoint(jni, clazz, declaring, callerClass, "objectFieldOffset", validResult, state.getFullStackTraceOrNull(), fromJniString(jni, name)); return true; } private static boolean getConstructor(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); JNIObjectHandle paramTypesHandle = getObjectArgument(1); JNIObjectHandle result = Support.callObjectMethodL(jni, self, bp.method, paramTypesHandle); if (clearException(jni)) { result = nullHandle(); } Object paramTypes = getClassArrayNames(jni, paramTypesHandle); traceBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, nullHandle().notEqual(result), state.getFullStackTraceOrNull(), paramTypes); return true; } private static boolean getMethod(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetMethod(jni, bp, false, state); } private static boolean getDeclaredMethod(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetMethod(jni, bp, true, state); } private static boolean handleGetMethod(JNIEnvironment jni, Breakpoint bp, boolean declaredOnly, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); JNIObjectHandle nameHandle = getObjectArgument(1); JNIObjectHandle paramTypesHandle = getObjectArgument(2); JNIObjectHandle result = Support.callObjectMethodLL(jni, self, bp.method, nameHandle, paramTypesHandle); if (clearException(jni)) { result = nullHandle(); } JNIObjectHandle declaring = nullHandle(); if (!declaredOnly && result.notEqual(nullHandle())) { declaring = Support.callObjectMethod(jni, result, agent.handles().javaLangReflectMemberGetDeclaringClass); if (clearException(jni)) { declaring = nullHandle(); } } String name = fromJniString(jni, nameHandle); Object paramTypes = getClassArrayNames(jni, paramTypesHandle); traceBreakpoint(jni, self, declaring, callerClass, bp.specification.methodName, result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name, paramTypes); return true; } private static boolean getEnclosingMethod(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); Object result = Tracer.EXPLICIT_NULL; JNIObjectHandle enclosing = Support.callObjectMethod(jni, self, bp.method); String name; String signature; if (!clearException(jni) && enclosing.notEqual(nullHandle())) { result = Tracer.UNKNOWN_VALUE; JNIMethodId enclosingID = jniFunctions().getFromReflectedMethod().invoke(jni, enclosing); if (!clearException(jni) && enclosingID.isNonNull()) { WordPointer holderPtr = StackValue.get(WordPointer.class); if (jvmtiFunctions().GetMethodDeclaringClass().invoke(jvmtiEnv(), enclosingID, holderPtr) == JvmtiError.JVMTI_ERROR_NONE) { String holderName = getClassNameOrNull(jni, holderPtr.read()); if (holderName != null) { CCharPointerPointer namePtr = StackValue.get(CCharPointerPointer.class); CCharPointerPointer signaturePtr = StackValue.get(CCharPointerPointer.class); if (jvmtiFunctions().GetMethodName().invoke(jvmtiEnv(), enclosingID, namePtr, signaturePtr, nullPointer()) == JvmtiError.JVMTI_ERROR_NONE) { name = fromCString(namePtr.read()); signature = fromCString(signaturePtr.read()); result = holderName + "." + name + signature; jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), namePtr.read()); jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), signaturePtr.read()); } } } } } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, bp.specification.methodName, enclosing.notEqual(nullHandle()) ? result : false, state.getFullStackTraceOrNull()); return true; } private static boolean invokeMethod(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleInvokeMethod(jni, bp, state, true); } private static boolean unreflectMethod(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleInvokeMethod(jni, bp, state, false); } private static boolean handleInvokeMethod(JNIEnvironment jni, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state, boolean isInvoke) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle method = getObjectArgument(isInvoke ? 0 : 1); JNIObjectHandle declaring = Support.callObjectMethod(jni, method, agent.handles().javaLangReflectMemberGetDeclaringClass); if (clearException(jni)) { declaring = nullHandle(); } JNIObjectHandle nameHandle = Support.callObjectMethod(jni, method, agent.handles().javaLangReflectMemberGetName); if (clearException(jni)) { nameHandle = nullHandle(); } String name = fromJniString(jni, nameHandle); JNIObjectHandle paramTypesHandle = Support.callObjectMethod(jni, method, agent.handles().getJavaLangReflectExecutableGetParameterTypes(jni)); if (clearException(jni)) { paramTypesHandle = nullHandle(); } Object paramTypes = getClassArrayNames(jni, paramTypesHandle); traceBreakpoint(jni, declaring, declaring, callerClass, "invokeMethod", declaring.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name, paramTypes); /* * Calling Class.newInstance through Method.invoke should register the class for reflective * instantiation */ if (isInvoke && isClassNewInstance(jni, declaring, name)) { JNIObjectHandle clazz = getObjectArgument(1); JNIMethodId result = newInstanceMethodID(jni, clazz); traceBreakpoint(jni, clazz, nullHandle(), callerClass, "newInstance", result.notEqual(nullHandle()), state.getFullStackTraceOrNull()); } return true; } private static boolean isClassNewInstance(JNIEnvironment jni, JNIObjectHandle declaring, String name) { if (!"newInstance".equals(name)) { return false; } JNIObjectHandle classNameHandle = Support.callObjectMethod(jni, declaring, agent.handles().javaLangClassGetName); if (clearException(jni)) { classNameHandle = nullHandle(); } String className = fromJniString(jni, classNameHandle); return "java.lang.Class".equals(className); } private static boolean invokeConstructor(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleInvokeConstructor(jni, bp, state, getObjectArgument(0)); } private static boolean unreflectConstructor(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleInvokeConstructor(jni, bp, state, getObjectArgument(1)); } private static boolean handleInvokeConstructor(JNIEnvironment jni, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state, JNIObjectHandle constructor) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle declaring = Support.callObjectMethod(jni, constructor, agent.handles().javaLangReflectMemberGetDeclaringClass); if (clearException(jni)) { declaring = nullHandle(); } JNIObjectHandle paramTypesHandle = Support.callObjectMethod(jni, constructor, agent.handles().getJavaLangReflectExecutableGetParameterTypes(jni)); if (clearException(jni)) { paramTypesHandle = nullHandle(); } Object paramTypes = getClassArrayNames(jni, paramTypesHandle); traceBreakpoint(jni, declaring, declaring, callerClass, "invokeConstructor", declaring.notEqual(nullHandle()), state.getFullStackTraceOrNull(), paramTypes); return true; } private static boolean newInstance(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); JNIMethodId result = newInstanceMethodID(jni, self); traceBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, result.notEqual(nullHandle()), state.getFullStackTraceOrNull()); return true; } private static JNIMethodId newInstanceMethodID(JNIEnvironment jni, JNIObjectHandle clazz) { JNIMethodId result = nullPointer(); String name = ""; String signature = "()V"; if (clazz.notEqual(nullHandle())) { try (CCharPointerHolder ctorName = toCString(name); CCharPointerHolder ctorSignature = toCString(signature)) { result = jniFunctions().getGetMethodID().invoke(jni, clazz, ctorName.get(), ctorSignature.get()); } if (clearException(jni)) { result = nullPointer(); } } return result; } private static boolean newArrayInstance(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle componentClass = getObjectArgument(0); CIntPointer lengthPtr = StackValue.get(CIntPointer.class); boolean lengthValid = (jvmtiFunctions().GetLocalInt().invoke(jvmtiEnv(), nullHandle(), 0, 1, lengthPtr) == JvmtiError.JVMTI_ERROR_NONE); JNIValue args = StackValue.get(2, JNIValue.class); args.addressOf(0).setObject(componentClass); args.addressOf(1).setInt(lengthPtr.read()); return newArrayInstance0(jni, bp, args, lengthValid, state); } private static boolean newArrayInstanceMulti(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle componentClass = getObjectArgument(0); JNIObjectHandle dimensionsArray = getObjectArgument(1); JNIValue args = StackValue.get(2, JNIValue.class); args.addressOf(0).setObject(componentClass); args.addressOf(1).setObject(dimensionsArray); return newArrayInstance0(jni, bp, args, dimensionsArray.notEqual(nullHandle()), state); } private static boolean newArrayInstance0(JNIEnvironment jni, Breakpoint bp, JNIValue args, boolean argsValid, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle result = nullHandle(); JNIObjectHandle resultClass = nullHandle(); JNIObjectHandle componentClass = args.addressOf(0).getObject(); if (componentClass.notEqual(nullHandle()) && argsValid) { result = jniFunctions().getCallStaticObjectMethodA().invoke(jni, bp.clazz, bp.method, args); if (clearException(jni)) { result = nullHandle(); } else { resultClass = jniFunctions().getGetObjectClass().invoke(jni, result); if (clearException(jni)) { resultClass = nullHandle(); } } } String resultClassName = getClassNameOr(jni, resultClass, null, Tracer.UNKNOWN_VALUE); traceBreakpoint(jni, bp.clazz, nullHandle(), callerClass, bp.specification.methodName, result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), resultClassName); return true; } private static boolean getResource(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetResources(jni, bp, false, state); } private static boolean getResources(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetResources(jni, bp, true, state); } private static boolean handleGetResources(JNIEnvironment jni, Breakpoint bp, boolean returnsEnumeration, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getObjectArgument(0); JNIObjectHandle name = getObjectArgument(1); boolean result; JNIObjectHandle returnValue = Support.callObjectMethodL(jni, self, bp.method, name); result = returnValue.notEqual(nullHandle()); if (clearException(jni)) { result = false; } if (result && returnsEnumeration) { result = hasEnumerationElements(jni, returnValue); } JNIObjectHandle selfClazz = nullHandle(); // self is java.lang.ClassLoader, get its class if (self.notEqual(nullHandle())) { selfClazz = jniFunctions().getGetObjectClass().invoke(jni, self); if (clearException(jni)) { selfClazz = nullHandle(); } } traceBreakpoint(jni, selfClazz, nullHandle(), callerClass, bp.specification.methodName, result, state.getFullStackTraceOrNull(), fromJniString(jni, name)); return true; } private static boolean hasEnumerationElements(JNIEnvironment jni, JNIObjectHandle obj) { boolean hasElements = Support.callBooleanMethod(jni, obj, agent.handles().javaUtilEnumerationHasMoreElements); if (clearException(jni)) { hasElements = false; } return hasElements; } private static boolean getSystemResource(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetSystemResources(jni, bp, false, state); } private static boolean getSystemResources(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { return handleGetSystemResources(jni, bp, true, state); } private static boolean handleGetSystemResources(JNIEnvironment jni, Breakpoint bp, boolean returnsEnumeration, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle name = getObjectArgument(0); JNIObjectHandle returnValue = Support.callStaticObjectMethodL(jni, bp.clazz, bp.method, name); boolean result = returnValue.notEqual(nullHandle()); if (clearException(jni)) { result = false; } if (result && returnsEnumeration) { result = hasEnumerationElements(jni, returnValue); } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, bp.specification.methodName, result, state.getFullStackTraceOrNull(), fromJniString(jni, name)); return true; } private static boolean newProxyInstance(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle classLoader = getObjectArgument(0); JNIObjectHandle ifaces = getObjectArgument(1); Object ifaceNames = getClassArrayNames(jni, ifaces); JNIObjectHandle invokeHandler = getObjectArgument(2); boolean result = nullHandle().notEqual(Support.callStaticObjectMethodLLL(jni, bp.clazz, bp.method, classLoader, ifaces, invokeHandler)); if (clearException(jni)) { result = false; } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, bp.specification.methodName, result, state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, ifaceNames, Tracer.UNKNOWN_VALUE); return true; } private static boolean getProxyClass(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle classLoader = getObjectArgument(0); JNIObjectHandle ifaces = getObjectArgument(1); Object ifaceNames = getClassArrayNames(jni, ifaces); boolean result = nullHandle().notEqual(Support.callStaticObjectMethodLL(jni, bp.clazz, bp.method, classLoader, ifaces)); if (clearException(jni)) { result = false; } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, bp.specification.methodName, result, state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, ifaceNames); return true; } private static Object getClassArrayNames(JNIEnvironment jni, JNIObjectHandle classArray) { Object classNames = Tracer.EXPLICIT_NULL; if (classArray.notEqual(nullHandle())) { classNames = Tracer.UNKNOWN_VALUE; int length = jniFunctions().getGetArrayLength().invoke(jni, classArray); if (!clearException(jni) && length >= 0) { List list = new ArrayList<>(); for (int i = 0; i < length; i++) { JNIObjectHandle clazz = jniFunctions().getGetObjectArrayElement().invoke(jni, classArray, i); if (!clearException(jni)) { list.add(getClassNameOr(jni, clazz, Tracer.EXPLICIT_NULL, Tracer.UNKNOWN_VALUE)); } else { list.add(Tracer.UNKNOWN_VALUE); } } classNames = list.toArray(new String[0]); } } return classNames; } private static boolean getBundleImplJDK8OrEarlier(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { /* actual caller of a getBundle method */ JNIObjectHandle callerClass = state.getCallerClass(2); JNIObjectHandle baseName = getObjectArgument(0); JNIObjectHandle locale = getObjectArgument(1); JNIObjectHandle loader = getObjectArgument(2); JNIObjectHandle control = getObjectArgument(3); JNIObjectHandle result = Support.callStaticObjectMethodLLLL(jni, bp.clazz, bp.method, baseName, locale, loader, control); if (clearException(jni)) { result = nullHandle(); } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK8OrEarlier", result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE); return true; } private static boolean getBundleImplJDK11OrLater(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIMethodId intermediateMethod = state.getCallerMethod(2); JNIMethodId callerMethod; // caller of getBundle(), not immediate caller if (intermediateMethod.equal(agent.handles().tryGetJavaUtilResourceBundleGetBundleImplSLCC(jni))) { // getBundleImpl <- getBundleImpl <- getBundleImpl(S,L,C,C) <- getBundle <- [caller] callerMethod = state.getCallerMethod(4); } else { // getBundleImpl <- getBundle(Impl|FromModule) <- getBundle <- [caller] callerMethod = state.getCallerMethod(3); } JNIObjectHandle callerClass = getMethodDeclaringClass(callerMethod); JNIObjectHandle callerModule = getObjectArgument(0); JNIObjectHandle module = getObjectArgument(1); JNIObjectHandle baseName = getObjectArgument(2); JNIObjectHandle locale = getObjectArgument(3); JNIObjectHandle control = getObjectArgument(4); JNIObjectHandle result = Support.callStaticObjectMethodLLLLL(jni, bp.clazz, bp.method, callerModule, module, baseName, locale, control); if (clearException(jni)) { result = nullHandle(); } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK11OrLater", result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE); return true; } private static boolean loadClass(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { assert experimentalClassLoaderSupport; /* * There is no easy way to tell if it was the virtual machine that called the class loader * because if so, the caller is simply the Java method that triggered loading the class. We * have to check the current bytecode in the caller method whether it is in fact a call to * loadClass(). */ JNIObjectHandle callerClass = nullHandle(); JvmtiFrameInfo frameInfo = StackValue.get(JvmtiFrameInfo.class); CIntPointer frameCountPtr = StackValue.get(CIntPointer.class); if (jvmtiFunctions().GetStackTrace().invoke(jvmtiEnv(), nullHandle(), 1, 1, (WordPointer) frameInfo, frameCountPtr) == JvmtiError.JVMTI_ERROR_NONE && frameCountPtr.read() == 1) { callerClass = getMethodDeclaringClass(frameInfo.getMethod()); if (callerClass.notEqual(nullHandle()) && jniFunctions().getIsAssignableFrom().invoke(jni, callerClass, agent.handles().javaLangClassLoader)) { // ignore recursive class loader calls, we must have seen the root invocation return true; } MethodLocation location = new MethodLocation(frameInfo.getMethod(), NumUtil.safeToInt(frameInfo.getLocation())); if (!observedExplicitLoadClassCallSites.containsKey(location)) { if (!isLoadClassInvocation(callerClass, location.method, location.bci, bp.specification.methodName, bp.specification.signature)) { return true; } observedExplicitLoadClassCallSites.put(location, Boolean.TRUE); } } JNIObjectHandle self = getObjectArgument(0); JNIObjectHandle name = getObjectArgument(1); String className = fromJniString(jni, name); JNIObjectHandle clazz = Support.callObjectMethodL(jni, self, bp.method, name); if (clearException(jni)) { clazz = nullHandle(); } traceBreakpoint(jni, bp.clazz, nullHandle(), callerClass, bp.specification.methodName, clazz.notEqual(nullHandle()), state.getFullStackTraceOrNull(), className); return true; } private static boolean isLoadClassInvocation(JNIObjectHandle clazz, JNIMethodId method, int bci, String methodName, String signature) { CIntPointer lengthPtr = StackValue.get(CIntPointer.class); CCharPointerPointer bytecodesPtr = StackValue.get(CCharPointerPointer.class); if (jvmtiFunctions().GetBytecodes().invoke(jvmtiEnv(), method, lengthPtr, bytecodesPtr) != JvmtiError.JVMTI_ERROR_NONE) { return false; } int cpi; CCharPointer bytecodes = bytecodesPtr.read(); try { if (bci + 2 /* index bytes */ >= lengthPtr.read()) { return false; } int instruction = Byte.toUnsignedInt(bytecodes.read(bci)); if (instruction != 0xb6) { // invokevirtual return false; } /* * According to Java VM Specification section 5.5, these checks should be sufficient * because invokevirtual should not trigger loading a class, but we still see cases * where this happens, so we further look at invoked method invoked at that location... */ int indexbyte1 = Byte.toUnsignedInt(bytecodes.read(bci + 1)); int indexbyte2 = Byte.toUnsignedInt(bytecodes.read(bci + 2)); cpi = (indexbyte1 << 8) | indexbyte2; } finally { jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), bytecodes); } CIntPointer constantPoolCountPtr = StackValue.get(CIntPointer.class); CIntPointer constantPoolByteCountPtr = StackValue.get(CIntPointer.class); CCharPointerPointer constantPoolBytesPtr = StackValue.get(CCharPointerPointer.class); if (jvmtiFunctions().GetConstantPool().invoke(jvmtiEnv(), clazz, constantPoolCountPtr, constantPoolByteCountPtr, constantPoolBytesPtr) != JvmtiError.JVMTI_ERROR_NONE) { return false; } CCharPointer constantPool = constantPoolBytesPtr.read(); try { ByteBuffer buffer = CTypeConversion.asByteBuffer(constantPool, constantPoolByteCountPtr.read()); buffer.order(ByteOrder.BIG_ENDIAN); try { MethodReference ref = new ConstantPoolTool(buffer).readMethodReference(cpi); return methodName.contentEquals(ref.name) && signature.contentEquals(ref.descriptor); } catch (ConstantPoolTool.ConstantPoolException e) { return false; // unsupported class file format? } } finally { jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), constantPool); } } private static boolean findMethodHandle(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle lookup = getObjectArgument(0); JNIObjectHandle declaringClass = getObjectArgument(1); JNIObjectHandle methodName = getObjectArgument(2); JNIObjectHandle methodType = getObjectArgument(3); JNIObjectHandle result = Support.callObjectMethodLLL(jni, lookup, bp.method, declaringClass, methodName, methodType); result = shouldIncludeMethod(jni, result, agent.handles().javaLangIllegalAccessException); return methodMethodHandle(jni, declaringClass, callerClass, methodName, getParamTypes(jni, methodType), result, state.getFullStackTraceOrNull()); } private static boolean findSpecialHandle(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle lookup = getObjectArgument(0); JNIObjectHandle declaringClass = getObjectArgument(1); JNIObjectHandle methodName = getObjectArgument(2); JNIObjectHandle methodType = getObjectArgument(3); JNIObjectHandle specialCaller = getObjectArgument(4); JNIObjectHandle result = Support.callObjectMethodLLLL(jni, lookup, bp.method, declaringClass, methodName, methodType, specialCaller); result = shouldIncludeMethod(jni, result, agent.handles().javaLangIllegalAccessException); return methodMethodHandle(jni, declaringClass, callerClass, methodName, getParamTypes(jni, methodType), result, state.getFullStackTraceOrNull()); } private static boolean bindHandle(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle lookup = getObjectArgument(0); JNIObjectHandle receiver = getObjectArgument(1); JNIObjectHandle methodName = getObjectArgument(2); JNIObjectHandle methodType = getObjectArgument(3); JNIObjectHandle result = Support.callObjectMethodLLL(jni, lookup, bp.method, receiver, methodName, methodType); result = shouldIncludeMethod(jni, result, agent.handles().javaLangIllegalAccessException); JNIObjectHandle declaringClass = Support.callObjectMethod(jni, receiver, agent.handles().javaLangObjectGetClass); if (clearException(jni)) { declaringClass = nullHandle(); } return methodMethodHandle(jni, declaringClass, callerClass, methodName, getParamTypes(jni, methodType), result, state.getFullStackTraceOrNull()); } private static boolean methodMethodHandle(JNIEnvironment jni, JNIObjectHandle declaringClass, JNIObjectHandle callerClass, JNIObjectHandle nameHandle, JNIObjectHandle paramTypesHandle, JNIObjectHandle result, JNIMethodId[] stackTrace) { String name = fromJniString(jni, nameHandle); Object paramTypes = getClassArrayNames(jni, paramTypesHandle); traceBreakpoint(jni, declaringClass, nullHandle(), callerClass, "findMethodHandle", result.notEqual(nullHandle()), stackTrace, name, paramTypes); return true; } private static boolean findConstructorHandle(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle lookup = getObjectArgument(0); JNIObjectHandle declaringClass = getObjectArgument(1); JNIObjectHandle methodType = getObjectArgument(2); JNIObjectHandle result = Support.callObjectMethodLL(jni, lookup, bp.method, declaringClass, methodType); result = shouldIncludeMethod(jni, result, agent.handles().javaLangIllegalAccessException); Object paramTypes = getClassArrayNames(jni, getParamTypes(jni, methodType)); traceBreakpoint(jni, declaringClass, nullHandle(), callerClass, "findConstructorHandle", result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), paramTypes); return true; } private static JNIObjectHandle getParamTypes(JNIEnvironment jni, JNIObjectHandle methodType) { JNIObjectHandle paramTypesHandle = Support.callObjectMethod(jni, methodType, agent.handles().getJavaLangInvokeMethodTypeParameterArray(jni)); if (clearException(jni)) { paramTypesHandle = nullHandle(); } return paramTypesHandle; } private static boolean findFieldHandle(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle lookup = getObjectArgument(0); JNIObjectHandle declaringClass = getObjectArgument(1); JNIObjectHandle fieldName = getObjectArgument(2); JNIObjectHandle fieldType = getObjectArgument(3); JNIObjectHandle result = Support.callObjectMethodLLL(jni, lookup, bp.method, declaringClass, fieldName, fieldType); result = shouldIncludeMethod(jni, result, agent.handles().javaLangIllegalAccessException); String name = fromJniString(jni, fieldName); traceBreakpoint(jni, declaringClass, nullHandle(), callerClass, "findFieldHandle", result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name); return true; } private static boolean findClass(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle lookup = getObjectArgument(0); JNIObjectHandle className = getObjectArgument(1); JNIObjectHandle result = Support.callObjectMethodL(jni, lookup, bp.method, className); result = shouldIncludeMethod(jni, result, agent.handles().javaLangIllegalAccessException); String name = fromJniString(jni, className); traceBreakpoint(jni, bp.clazz, nullHandle(), callerClass, "findClass", result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name); return true; } private static boolean unreflectField(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle lookup = getObjectArgument(0); JNIObjectHandle field = getObjectArgument(1); JNIObjectHandle result = Support.callObjectMethodL(jni, lookup, bp.method, field); result = shouldIncludeMethod(jni, result, agent.handles().javaLangIllegalAccessException); JNIObjectHandle declaringClass = Support.callObjectMethod(jni, field, agent.handles().javaLangReflectMemberGetDeclaringClass); if (clearException(jni)) { declaringClass = nullHandle(); } JNIObjectHandle fieldNameHandle = Support.callObjectMethod(jni, field, agent.handles().javaLangReflectMemberGetName); if (clearException(jni)) { fieldNameHandle = nullHandle(); } String fieldName = fromJniString(jni, fieldNameHandle); traceBreakpoint(jni, declaringClass, nullHandle(), callerClass, "unreflectField", result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), fieldName); return true; } private static boolean asInterfaceInstance(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle intfc = getObjectArgument(0); JNIObjectHandle methodHandle = getObjectArgument(1); JNIObjectHandle result = Support.callStaticObjectMethodLL(jni, bp.clazz, bp.method, intfc, methodHandle); result = shouldIncludeMethod(jni, result, agent.handles().javaLangInvokeWrongMethodTypeException, agent.handles().javaLangIllegalArgumentException); JNIObjectHandle intfcNameHandle = Support.callObjectMethod(jni, intfc, agent.handles().javaLangClassGetName); if (clearException(jni)) { intfcNameHandle = nullHandle(); } String intfcName = fromJniString(jni, intfcNameHandle); traceBreakpoint(jni, intfc, nullHandle(), callerClass, "asInterfaceInstance", result.notEqual(nullHandle()), state.getFullStackTraceOrNull()); String[] intfcNames = new String[]{intfcName}; traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "newMethodHandleProxyInstance", result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), (Object) intfcNames); return true; } private static boolean constantBootstrapGetStaticFinal(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle lookup = getObjectArgument(0); JNIObjectHandle fieldName = getObjectArgument(1); JNIObjectHandle type = getObjectArgument(2); JNIObjectHandle declaringClass = getObjectArgument(3); JNIObjectHandle result = Support.callStaticObjectMethodLLLL(jni, bp.clazz, bp.method, lookup, fieldName, type, declaringClass); result = shouldIncludeMethod(jni, result, agent.handles().javaLangIllegalAccessException); String name = fromJniString(jni, fieldName); traceBreakpoint(jni, declaringClass, nullHandle(), callerClass, "findFieldHandle", result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name); return true; } private static boolean methodTypeFromDescriptor(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle descriptor = getObjectArgument(0); JNIObjectHandle classLoader = getObjectArgument(1); JNIObjectHandle result = Support.callStaticObjectMethodLL(jni, bp.clazz, bp.method, descriptor, classLoader); if (clearException(jni)) { result = nullHandle(); } List types = new ArrayList<>(); if (result.notEqual(nullHandle())) { JNIObjectHandle rtype = Support.callObjectMethod(jni, result, agent.handles().getJavaLangInvokeMethodTypeReturnType(jni)); if (clearException(jni)) { rtype = nullHandle(); } String rtypeName = getClassNameOrNull(jni, rtype); if (rtypeName != null) { types.add(rtypeName); } JNIObjectHandle ptypes = Support.callObjectMethod(jni, result, agent.handles().getJavaLangInvokeMethodTypeParameterArray(jni)); if (clearException(jni)) { ptypes = nullHandle(); } Object ptypeNames = getClassArrayNames(jni, ptypes); if (ptypeNames instanceof String[]) { types.addAll(Arrays.asList((String[]) ptypeNames)); } } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "methodTypeDescriptor", result.notEqual(nullHandle()), state.getFullStackTraceOrNull(), types); return true; } private static JNIObjectHandle shouldIncludeMethod(JNIEnvironment jni, JNIObjectHandle result, JNIObjectHandle... acceptedExceptions) { JNIObjectHandle exception = handleException(jni, true); if (exception.notEqual(nullHandle())) { for (JNIObjectHandle acceptedException : acceptedExceptions) { if (jniFunctions().getIsInstanceOf().invoke(jni, exception, acceptedException)) { /* * We include methods if the lookup returned an IllegalAccessException or a * WrongMethodTypeException to make sure the right exception is thrown at * runtime, instead of a NoSuchMethodException. */ return JNIObjectHandles.createLocal(Boolean.TRUE); } } return nullHandle(); } return result; } private static boolean objectStreamClassConstructor(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { JNIObjectHandle serializeTargetClass = getObjectArgument(1); String serializeTargetClassName = getClassNameOrNull(jni, serializeTargetClass); JNIObjectHandle objectStreamClassInstance = newObjectL(jni, bp.clazz, bp.method, serializeTargetClass); boolean validObjectStreamClassInstance = nullHandle().notEqual(objectStreamClassInstance); if (clearException(jni)) { validObjectStreamClassInstance = false; } // Skip Lambda class serialization if (serializeTargetClassName.contains("$$Lambda$")) { return true; } List transitiveSerializeTargets = new ArrayList<>(); transitiveSerializeTargets.add(serializeTargetClassName); /* * When the ObjectStreamClass instance is created for the given serializeTargetClass, some * additional ObjectStreamClass instances (usually the super classes) are created * recursively. Call ObjectStreamClass.getClassDataLayout0() can get all of them. */ JNIMethodId getClassDataLayout0MId = agent.handles().getJavaIoObjectStreamClassGetClassDataLayout0(jni, bp.clazz); JNIObjectHandle dataLayoutArray = callObjectMethod(jni, objectStreamClassInstance, getClassDataLayout0MId); if (!clearException(jni) && nullHandle().notEqual(dataLayoutArray)) { int length = jniFunctions().getGetArrayLength().invoke(jni, dataLayoutArray); // If only 1 element is got from getClassDataLayout0(). it is base ObjectStreamClass // instance itself. if (!clearException(jni) && length > 1) { JNIFieldId hasDataFId = agent.handles().getJavaIOObjectStreamClassClassDataSlotHasData(jni); JNIFieldId descFId = agent.handles().getJavaIOObjectStreamClassClassDataSlotDesc(jni); JNIMethodId javaIoObjectStreamClassForClassMId = agent.handles().getJavaIoObjectStreamClassForClass(jni, bp.clazz); for (int i = 0; i < length; i++) { JNIObjectHandle classDataSlot = jniFunctions().getGetObjectArrayElement().invoke(jni, dataLayoutArray, i); boolean hasData = jniFunctions().getGetBooleanField().invoke(jni, classDataSlot, hasDataFId); if (hasData) { JNIObjectHandle oscInstanceInSlot = jniFunctions().getGetObjectField().invoke(jni, classDataSlot, descFId); if (!jniFunctions().getIsSameObject().invoke(jni, oscInstanceInSlot, objectStreamClassInstance)) { JNIObjectHandle oscClazz = callObjectMethod(jni, oscInstanceInSlot, javaIoObjectStreamClassForClassMId); String oscClassName = getClassNameOrNull(jni, oscClazz); transitiveSerializeTargets.add(oscClassName); } } } } } for (String className : transitiveSerializeTargets) { if (tracer != null) { tracer.traceCall("serialization", "ObjectStreamClass.", null, null, null, validObjectStreamClassInstance, state.getFullStackTraceOrNull(), /*- String serializationTargetClass, String customTargetConstructorClass */ className, null); guarantee(!testException(jni)); } } return true; } /** * In rare occasions, the application can demand custom target constructor for serialization, * using sun.reflect.ReflectionFactory#newConstructorForSerialization(java.lang.Class, * java.lang.reflect.Constructor) on JDK8 or * jdk.internal.reflect.ReflectionFactory#newConstructorForSerialization(java.lang.Class, * java.lang.reflect.Constructor) on JDK11. We need to catch constructor class and create entry * for that pair (serialization class, custom class constructor) in serialization configuration. */ private static boolean customTargetConstructorSerialization(JNIEnvironment jni, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) { JNIObjectHandle serializeTargetClass = getObjectArgument(1); String serializeTargetClassName = getClassNameOrNull(jni, serializeTargetClass); // Skip Lambda class serialization. if (serializeTargetClassName.contains("$$Lambda$")) { return true; } JNIObjectHandle customConstructorObj = getObjectArgument(2); JNIObjectHandle customConstructorClass = jniFunctions().getGetObjectClass().invoke(jni, customConstructorObj); JNIMethodId getDeclaringClassNameMethodID = agent.handles().getJavaLangReflectConstructorDeclaringClassName(jni, customConstructorClass); JNIObjectHandle declaredClassNameObj = callObjectMethod(jni, customConstructorObj, getDeclaringClassNameMethodID); String customConstructorClassName = fromJniString(jni, declaredClassNameObj); if (tracer != null) { tracer.traceCall("serialization", "ObjectStreamClass.", null, null, null, true, state.getFullStackTraceOrNull(), /*- String serializationTargetClass, String customTargetConstructorClass */ serializeTargetClassName, customConstructorClassName); guarantee(!testException(jni)); } return true; } @CEntryPoint @CEntryPointOptions(prologue = AgentIsolate.Prologue.class) private static void onBreakpoint(@SuppressWarnings("unused") JvmtiEnv jvmti, JNIEnvironment jni, @SuppressWarnings("unused") JNIObjectHandle thread, JNIMethodId method, @SuppressWarnings("unused") long location) { if (recursive.get()) { return; } recursive.set(true); try { Breakpoint bp = installedBreakpoints.get(method.rawValue()); InterceptedState state = interceptedStateSupplier.get(); if (bp.specification.handler.dispatch(jni, bp, state)) { guarantee(!testException(jni)); } } catch (Throwable t) { VMError.shouldNotReachHere(t); } finally { recursive.set(false); } } @CEntryPoint @CEntryPointOptions(prologue = AgentIsolate.Prologue.class) private static void onClassPrepare(@SuppressWarnings("unused") JvmtiEnv jvmti, JNIEnvironment jni, @SuppressWarnings("unused") JNIObjectHandle thread, JNIObjectHandle clazz) { assert experimentalClassLoaderSupport; installBreakpointIfClassLoader(jni, clazz, installedBreakpoints); } private static void installBreakpointIfClassLoader(JNIEnvironment jni, JNIObjectHandle clazz, Map breakpoints) { if (jniFunctions().getIsAssignableFrom().invoke(jni, clazz, agent.handles().javaLangClassLoader)) { String className = getClassNameOrNull(jni, clazz); if (className != null) { BreakpointSpecification proto = CLASSLOADER_LOAD_CLASS_BREAKPOINT_SPECIFICATION; JNIMethodId method = resolveBreakpointMethod(jni, clazz, proto.methodName, proto.signature, true); if (method.isNonNull() && jvmtiFunctions().SetBreakpoint().invoke(jvmtiEnv(), method, 0L) == JvmtiError.JVMTI_ERROR_NONE) { BreakpointSpecification spec = new BreakpointSpecification(className, proto.methodName, proto.signature, proto.handler, proto.optional); JNIObjectHandle gclazz = agent.handles().newTrackedGlobalRef(jni, clazz); breakpoints.put(method.rawValue(), new Breakpoint(spec, gclazz, method)); } } } } @CEntryPoint @CEntryPointOptions(prologue = AgentIsolate.Prologue.class) private static void onClassFileLoadHook(@SuppressWarnings("unused") JvmtiEnv jvmti, JNIEnvironment jni, @SuppressWarnings("unused") JNIObjectHandle classBeingRedefined, JNIObjectHandle loader, CCharPointer name, @SuppressWarnings("unused") JNIObjectHandle protectionDomain, int classDataLen, CCharPointer classData, @SuppressWarnings("unused") CIntPointer newClassDataLen, @SuppressWarnings("unused") CCharPointerPointer newClassData) { InterceptedState state = interceptedStateSupplier.get(); if (loader.equal(nullHandle())) { // boot class loader return; } String className = fromCString(name); if (className != null && AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(className).matches()) { // Proxy classes are handled using a different mechanism, so we ignore them here return; } for (JNIObjectHandle builtinLoader : builtinClassLoaders) { if (jniFunctions().getIsSameObject().invoke(jni, loader, builtinLoader)) { return; } } if (jniFunctions().getIsInstanceOf().invoke(jni, loader, agent.handles().jdkInternalReflectDelegatingClassLoader)) { return; } byte[] data = new byte[classDataLen]; CTypeConversion.asByteBuffer(classData, classDataLen).get(data); tracer.traceCall("classloading", "onClassFileLoadHook", null, null, null, null, state.getFullStackTraceOrNull(), className, data); } private static final CEntryPointLiteral onBreakpointLiteral = CEntryPointLiteral.create(BreakpointInterceptor.class, "onBreakpoint", JvmtiEnv.class, JNIEnvironment.class, JNIObjectHandle.class, JNIMethodId.class, long.class); private static final CEntryPointLiteral onClassPrepareLiteral = CEntryPointLiteral.create(BreakpointInterceptor.class, "onClassPrepare", JvmtiEnv.class, JNIEnvironment.class, JNIObjectHandle.class, JNIObjectHandle.class); private static final CEntryPointLiteral onClassFileLoadHookLiteral = CEntryPointLiteral.create(BreakpointInterceptor.class, "onClassFileLoadHook", JvmtiEnv.class, JNIEnvironment.class, JNIObjectHandle.class, JNIObjectHandle.class, CCharPointer.class, JNIObjectHandle.class, int.class, CCharPointer.class, CIntPointer.class, CCharPointerPointer.class); public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, Tracer writer, NativeImageAgent nativeImageTracingAgent, Supplier currentThreadJavaStackAccessSupplier, boolean exptlClassLoaderSupport, boolean exptlClassDefineSupport, boolean trackReflectionData) { BreakpointInterceptor.tracer = writer; BreakpointInterceptor.agent = nativeImageTracingAgent; BreakpointInterceptor.interceptedStateSupplier = currentThreadJavaStackAccessSupplier; BreakpointInterceptor.experimentalClassLoaderSupport = exptlClassLoaderSupport; BreakpointInterceptor.experimentalClassDefineSupport = exptlClassDefineSupport; BreakpointInterceptor.trackReflectionMetadata = trackReflectionData; JvmtiCapabilities capabilities = UnmanagedMemory.calloc(SizeOf.get(JvmtiCapabilities.class)); check(jvmti.getFunctions().GetCapabilities().invoke(jvmti, capabilities)); capabilities.setCanGenerateBreakpointEvents(1); capabilities.setCanAccessLocalVariables(1); if (exptlClassLoaderSupport) { capabilities.setCanGetBytecodes(1); capabilities.setCanGetConstantPool(1); CIntPointer formatPtr = StackValue.get(CIntPointer.class); guarantee(jvmti.getFunctions().GetJLocationFormat().invoke(jvmti, formatPtr) == JvmtiError.JVMTI_ERROR_NONE && formatPtr.read() == JvmtiLocationFormat.JVMTI_JLOCATION_JVMBCI.getCValue(), "Expecting BCI locations"); } check(jvmti.getFunctions().AddCapabilities().invoke(jvmti, capabilities)); UnmanagedMemory.free(capabilities); callbacks.setBreakpoint(onBreakpointLiteral.getFunctionPointer()); if (exptlClassDefineSupport) { callbacks.setClassFileLoadHook(onClassFileLoadHookLiteral.getFunctionPointer()); } if (exptlClassLoaderSupport) { callbacks.setClassPrepare(onClassPrepareLiteral.getFunctionPointer()); } } public static void onVMInit(JvmtiEnv jvmti, JNIEnvironment jni) { Map breakpoints; if (experimentalClassLoaderSupport) { // Breakpoints are added when class loaders are added and must be thread-safe breakpoints = new ConcurrentHashMap<>(BREAKPOINT_SPECIFICATIONS.length); observedExplicitLoadClassCallSites = new ConcurrentHashMap<>(); // Now that we can set breakpoints, check all currently loaded classes CIntPointer classCountPtr = StackValue.get(CIntPointer.class); WordPointer classesPtr = StackValue.get(WordPointer.class); check(jvmtiFunctions().GetLoadedClasses().invoke(jvmti, classCountPtr, classesPtr)); WordPointer classesArray = classesPtr.read(); for (int i = 0; i < classCountPtr.read(); i++) { JNIObjectHandle clazz = classesArray.read(i); installBreakpointIfClassLoader(jni, clazz, breakpoints); } check(jvmtiFunctions().Deallocate().invoke(jvmti, classesArray)); } else { breakpoints = new HashMap<>(BREAKPOINT_SPECIFICATIONS.length); } JNIObjectHandle lastClass = nullHandle(); String lastClassName = null; BreakpointSpecification[] breakpointSpecifications = BREAKPOINT_SPECIFICATIONS; if (trackReflectionMetadata) { breakpointSpecifications = new BreakpointSpecification[BREAKPOINT_SPECIFICATIONS.length + REFLECTION_QUERIES_BREAKPOINT_SPECIFICATIONS.length]; System.arraycopy(BREAKPOINT_SPECIFICATIONS, 0, breakpointSpecifications, 0, BREAKPOINT_SPECIFICATIONS.length); System.arraycopy(REFLECTION_QUERIES_BREAKPOINT_SPECIFICATIONS, 0, breakpointSpecifications, BREAKPOINT_SPECIFICATIONS.length, REFLECTION_QUERIES_BREAKPOINT_SPECIFICATIONS.length); } for (BreakpointSpecification br : breakpointSpecifications) { JNIObjectHandle clazz = nullHandle(); if (lastClassName != null && lastClassName.equals(br.className)) { clazz = lastClass; } Breakpoint bp = installBreakpoint(jni, br, breakpoints, clazz); if (bp != null) { lastClass = bp.clazz; lastClassName = br.className; } } installedBreakpoints = breakpoints; if (experimentalClassDefineSupport) { setupClassLoadEvent(jvmti, jni); } check(jvmti.getFunctions().SetEventNotificationMode().invoke(jvmti, JvmtiEventMode.JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, nullHandle())); if (experimentalClassLoaderSupport) { check(jvmti.getFunctions().SetEventNotificationMode().invoke(jvmti, JvmtiEventMode.JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, nullHandle())); } } private static void setupClassLoadEvent(JvmtiEnv jvmti, JNIEnvironment jni) { JNIObjectHandle classLoader = agent.handles().javaLangClassLoader; JNIMethodId getSystemClassLoader = agent.handles().getMethodId(jni, classLoader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;", true); JNIObjectHandle systemLoader = Support.callStaticObjectMethod(jni, classLoader, getSystemClassLoader); checkNoException(jni); guarantee(systemLoader.notEqual(nullHandle())); JNIMethodId getPlatformLoader = agent.handles().getMethodIdOptional(jni, classLoader, "getPlatformClassLoader", "()Ljava/lang/ClassLoader;", true); JNIMethodId getAppLoader = agent.handles().getMethodIdOptional(jni, classLoader, "getBuiltinAppClassLoader", "()Ljava/lang/ClassLoader;", true); if (getPlatformLoader.isNonNull() && getAppLoader.isNonNull()) { // only on JDK 9 and later JNIObjectHandle platformLoader = callObjectMethod(jni, classLoader, getPlatformLoader); checkNoException(jni); JNIObjectHandle appLoader = callObjectMethod(jni, classLoader, getAppLoader); checkNoException(jni); guarantee(platformLoader.notEqual(nullHandle()) && appLoader.notEqual(nullHandle())); if (!jniFunctions().getIsSameObject().invoke(jni, systemLoader, appLoader)) { builtinClassLoaders = new JNIObjectHandle[3]; builtinClassLoaders[2] = agent.handles().newTrackedGlobalRef(jni, appLoader); } else { builtinClassLoaders = new JNIObjectHandle[2]; } builtinClassLoaders[1] = agent.handles().newTrackedGlobalRef(jni, platformLoader); } else { guarantee(getPlatformLoader.isNull() && getAppLoader.isNull()); builtinClassLoaders = new JNIObjectHandle[1]; } builtinClassLoaders[0] = agent.handles().newTrackedGlobalRef(jni, systemLoader); check(jvmti.getFunctions().SetEventNotificationMode().invoke(jvmti, JvmtiEventMode.JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, nullHandle())); } private static Breakpoint installBreakpoint(JNIEnvironment jni, BreakpointSpecification br, Map map, JNIObjectHandle knownClass) { JNIObjectHandle clazz = knownClass; if (clazz.equal(nullHandle())) { clazz = resolveBreakpointClass(jni, br.className, br.optional); if (clazz.equal(nullHandle())) { guarantee(br.optional); return null; } } JNIMethodId method = resolveBreakpointMethod(jni, clazz, br.methodName, br.signature, br.optional); JvmtiError result = jvmtiFunctions().SetBreakpoint().invoke(jvmtiEnv(), method, 0L); if (result != JvmtiError.JVMTI_ERROR_NONE) { guarantee(br.optional, "Setting breakpoint failed"); return null; } Breakpoint bp = new Breakpoint(br, clazz, method); guarantee(map.put(method.rawValue(), bp) == null, "Duplicate breakpoint: " + bp); return bp; } private static JNIObjectHandle resolveBreakpointClass(JNIEnvironment jni, String className, boolean optional) { JNIObjectHandle clazz; try (CCharPointerHolder cname = toCString(className)) { clazz = jniFunctions().getFindClass().invoke(jni, cname.get()); if (optional && (clearException(jni) || clazz.equal(nullHandle()))) { return nullHandle(); } checkNoException(jni); } clazz = agent.handles().newTrackedGlobalRef(jni, clazz); checkNoException(jni); return clazz; } private static JNIMethodId resolveBreakpointMethod(JNIEnvironment jni, JNIObjectHandle clazz, String methodName, String signature, boolean optional) { guarantee(clazz.notEqual(nullHandle())); JNIMethodId method; try (CCharPointerHolder cname = toCString(methodName); CCharPointerHolder csignature = toCString(signature)) { method = jniFunctions().getGetMethodID().invoke(jni, clazz, cname.get(), csignature.get()); if (method.isNull()) { clearException(jni); method = jniFunctions().getGetStaticMethodID().invoke(jni, clazz, cname.get(), csignature.get()); } } if (optional && (clearException(jni) || method.isNull())) { return nullPointer(); } guarantee(!testException(jni) && method.isNonNull()); return method; } public static void onUnload() { builtinClassLoaders = null; installedBreakpoints = null; observedExplicitLoadClassCallSites = null; tracer = null; } private interface BreakpointHandler { boolean dispatch(JNIEnvironment jni, Breakpoint bp, InterceptedState state); } private static final BreakpointSpecification[] BREAKPOINT_SPECIFICATIONS = { brk("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", BreakpointInterceptor::forName), brk("java/lang/Class", "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;", BreakpointInterceptor::forName), brk("java/lang/Class", "getFields", "()[Ljava/lang/reflect/Field;", BreakpointInterceptor::getFields), brk("java/lang/Class", "getClasses", "()[Ljava/lang/Class;", BreakpointInterceptor::getClasses), brk("java/lang/Class", "getDeclaredFields", "()[Ljava/lang/reflect/Field;", BreakpointInterceptor::getDeclaredFields), brk("java/lang/Class", "getDeclaredClasses", "()[Ljava/lang/Class;", BreakpointInterceptor::getDeclaredClasses), brk("java/lang/Class", "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", BreakpointInterceptor::getField), brk("java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", BreakpointInterceptor::getDeclaredField), brk("java/lang/Class", "getEnclosingMethod", "()Ljava/lang/reflect/Method;", BreakpointInterceptor::getEnclosingMethod), brk("java/lang/Class", "getEnclosingConstructor", "()Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getEnclosingMethod), brk("java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", BreakpointInterceptor::invokeMethod), brk("sun/reflect/misc/MethodUtil", "invoke", "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", BreakpointInterceptor::invokeMethod), brk("java/lang/reflect/Constructor", "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;", BreakpointInterceptor::invokeConstructor), brk("java/lang/Class", "newInstance", "()Ljava/lang/Object;", BreakpointInterceptor::newInstance), brk("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;", BreakpointInterceptor::newArrayInstance), brk("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;[I)Ljava/lang/Object;", BreakpointInterceptor::newArrayInstanceMulti), brk("java/lang/ClassLoader", "getResource", "(Ljava/lang/String;)Ljava/net/URL;", BreakpointInterceptor::getResource), brk("java/lang/ClassLoader", "getResources", "(Ljava/lang/String;)Ljava/util/Enumeration;", BreakpointInterceptor::getResources), brk("java/lang/ClassLoader", "getSystemResource", "(Ljava/lang/String;)Ljava/net/URL;", BreakpointInterceptor::getSystemResource), brk("java/lang/ClassLoader", "getSystemResources", "(Ljava/lang/String;)Ljava/util/Enumeration;", BreakpointInterceptor::getSystemResources), /* * NOTE: get(System)ResourceAsStream() generallys call get(System)Resource(), no * additional breakpoints necessary */ brk("java/lang/reflect/Proxy", "getProxyClass", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;)Ljava/lang/Class;", BreakpointInterceptor::getProxyClass), brk("java/lang/reflect/Proxy", "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;", BreakpointInterceptor::newProxyInstance), brk("java/io/ObjectStreamClass", "", "(Ljava/lang/Class;)V", BreakpointInterceptor::objectStreamClassConstructor), brk(Package_jdk_internal_reflect.getQualifiedName().replace(".", "/") + "/ReflectionFactory", "newConstructorForSerialization", "(Ljava/lang/Class;Ljava/lang/reflect/Constructor;)Ljava/lang/reflect/Constructor;", BreakpointInterceptor::customTargetConstructorSerialization), optionalBrk("java/util/ResourceBundle", "getBundleImpl", "(Ljava/lang/String;Ljava/util/Locale;Ljava/lang/ClassLoader;Ljava/util/ResourceBundle$Control;)Ljava/util/ResourceBundle;", BreakpointInterceptor::getBundleImplJDK8OrEarlier), optionalBrk("java/util/ResourceBundle", "getBundleImpl", "(Ljava/lang/Module;Ljava/lang/Module;Ljava/lang/String;Ljava/util/Locale;Ljava/util/ResourceBundle$Control;)Ljava/util/ResourceBundle;", BreakpointInterceptor::getBundleImplJDK11OrLater), // In Java 9+, these are Java methods that call private methods optionalBrk("jdk/internal/misc/Unsafe", "objectFieldOffset", "(Ljava/lang/Class;Ljava/lang/String;)J", BreakpointInterceptor::objectFieldOffsetByName), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::findMethodHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findVirtual", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::findMethodHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::findConstructorHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findSpecial", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::findSpecialHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "bind", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::bindHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findGetter", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::findFieldHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findSetter", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::findFieldHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findStaticGetter", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::findFieldHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findStaticSetter", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::findFieldHandle), /* VarHandles were introduced in Java 9 */ optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findVarHandle", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;", BreakpointInterceptor::findFieldHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findStaticVarHandle", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;", BreakpointInterceptor::findFieldHandle), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findClass", "(Ljava/lang/String;)Ljava/lang/Class;", BreakpointInterceptor::findClass), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "unreflect", "(Ljava/lang/reflect/Method;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::unreflectMethod), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "unreflectConstructor", "(Ljava/lang/reflect/Constructor;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::unreflectConstructor), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "unreflectGetter", "(Ljava/lang/reflect/Field;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::unreflectField), optionalBrk("java/lang/invoke/MethodHandles$Lookup", "unreflectSetter", "(Ljava/lang/reflect/Field;)Ljava/lang/invoke/MethodHandle;", BreakpointInterceptor::unreflectField), optionalBrk("java/lang/invoke/MethodHandleProxies", "asInterfaceInstance", "(Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;", BreakpointInterceptor::asInterfaceInstance), optionalBrk("java/lang/invoke/ConstantBootstraps", "getStaticFinal", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;", BreakpointInterceptor::constantBootstrapGetStaticFinal), optionalBrk("java/lang/invoke/MethodType", "fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;", BreakpointInterceptor::methodTypeFromDescriptor) }; private static final BreakpointSpecification CLASSLOADER_LOAD_CLASS_BREAKPOINT_SPECIFICATION = optionalBrk("java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", BreakpointInterceptor::loadClass); private static final BreakpointSpecification[] REFLECTION_QUERIES_BREAKPOINT_SPECIFICATIONS = { brk("java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;", BreakpointInterceptor::getMethods), brk("java/lang/Class", "getConstructors", "()[Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructors), brk("java/lang/Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;", BreakpointInterceptor::getDeclaredMethods), brk("java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getDeclaredConstructors), brk("java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", BreakpointInterceptor::getMethod), brk("java/lang/Class", "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructor), brk("java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", BreakpointInterceptor::getDeclaredMethod), brk("java/lang/Class", "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructor), }; private static BreakpointSpecification brk(String className, String methodName, String signature, BreakpointHandler handler) { return new BreakpointSpecification(className, methodName, signature, handler, false); } private static BreakpointSpecification optionalBrk(String className, String methodName, String signature, BreakpointHandler handler) { return new BreakpointSpecification(className, methodName, signature, handler, true); } private abstract static class AbstractBreakpointSpecification { final String className; final String methodName; final String signature; final boolean optional; AbstractBreakpointSpecification(String className, String methodName, String signature, boolean optional) { this.className = className; this.methodName = methodName; this.signature = signature; this.optional = optional; } @Override public String toString() { return className + ":" + methodName + signature + (optional ? " (optional)" : ""); } } private static class BreakpointSpecification extends AbstractBreakpointSpecification { final BreakpointHandler handler; BreakpointSpecification(String className, String methodName, String signature, BreakpointHandler handler, boolean optional) { super(className, methodName, signature, optional); this.handler = handler; } } private abstract static class AbstractBreakpoint { final T specification; final JNIObjectHandle clazz; final JNIMethodId method; AbstractBreakpoint(T specification, JNIObjectHandle clazz, JNIMethodId method) { this.specification = specification; this.clazz = clazz; this.method = method; } @Override public String toString() { return specification.toString(); } } private static class Breakpoint extends AbstractBreakpoint { Breakpoint(BreakpointSpecification specification, JNIObjectHandle clazz, JNIMethodId method) { super(specification, clazz, method); } } private static final class MethodLocation { final JNIMethodId method; final int bci; MethodLocation(JNIMethodId method, int bci) { this.method = method; this.bci = bci; } @Override public boolean equals(Object obj) { if (obj != this && obj instanceof MethodLocation) { MethodLocation other = (MethodLocation) obj; return method.equal(other.method) && bci == other.bci; } return (obj == this); } @Override public int hashCode() { return 31 * Long.hashCode(method.rawValue()) + bci; } } private BreakpointInterceptor() { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy