/*
 * Copyright (c) 2019, 2020, 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.jvmtiagentbase;

import static com.oracle.svm.core.util.VMError.guarantee;
import static com.oracle.svm.jni.JNIObjectHandles.nullHandle;

import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;

import com.oracle.svm.jni.nativeapi.JNIFieldId;
import org.graalvm.nativeimage.c.type.CTypeConversion;

import com.oracle.svm.jni.JNIObjectHandles;
import com.oracle.svm.jni.nativeapi.JNIEnvironment;
import com.oracle.svm.jni.nativeapi.JNIMethodId;
import com.oracle.svm.jni.nativeapi.JNIObjectHandle;

Helps with creation and management of JNI handles for JVMTI agents. A JVMTI agent must provide either this class or a subclass of this class. It should contain handles to classes and methods that are needed across different JVMTI calls (for example, a JNIObjectHandle to java/lang/Class). For JNI handles that are created in a JVMTI callback and that should survive a return from native code, a helper method newTrackedGlobalRef is provided.
See Also:
/** * Helps with creation and management of JNI handles for JVMTI agents. * * A JVMTI agent must provide either this class or a subclass of this class. It should contain * handles to classes and methods that are needed across different JVMTI calls (for example, a * JNIObjectHandle to java/lang/Class). * * For JNI handles that are created in a JVMTI callback and that should survive a return from native * code, a helper method {@link #newTrackedGlobalRef} is provided. * * @see JvmtiAgentBase */
public abstract class JNIHandleSet { private static final int INITIAL_GLOBAL_HANDLE_CAPACITY = 16; private final ReentrantLock globalRefsLock = new ReentrantLock(); private JNIObjectHandle[] globalRefs = new JNIObjectHandle[INITIAL_GLOBAL_HANDLE_CAPACITY]; private int globalRefCount = 0; private boolean destroyed = false; final JNIMethodId javaLangClassGetName; public JNIHandleSet(JNIEnvironment env) { JNIObjectHandle javaLangClass = findClass(env, "java/lang/Class"); try (CTypeConversion.CCharPointerHolder name = Support.toCString("getName"); CTypeConversion.CCharPointerHolder signature = Support.toCString("()Ljava/lang/String;")) { javaLangClassGetName = Support.jniFunctions().getGetMethodID().invoke(env, javaLangClass, name.get(), signature.get()); guarantee(javaLangClassGetName.isNonNull()); } }
Returns a local handle to a Java class object. The class must exist. If not found, the VM terminates.
Params:
  • env – JNI environment of the thread running the JVMTI callback.
  • className – The VM type signature of the class.
Returns:Local JNI handle representing the class object.
/** * Returns a local handle to a Java class object. * * The class must exist. If not found, the VM terminates. * * @param env JNI environment of the thread running the JVMTI callback. * @param className The VM type signature of the class. * @return Local JNI handle representing the class object. */
public JNIObjectHandle findClass(JNIEnvironment env, String className) { assert !destroyed; try (CTypeConversion.CCharPointerHolder name = Support.toCString(className)) { JNIObjectHandle h = Support.jniFunctions().getFindClass().invoke(env, name.get()); guarantee(h.notEqual(nullHandle())); return h; } }
A convenience method to return a global handle to a Java class object. The handle will be tracked and freed with a call to destroy. The reference must not be freed manually. The class must exist. If not found, the VM terminates.
Params:
  • env – JNI environment of the thread running the JVMTI callback.
  • className – Java VM type signature of the class.
See Also:
Returns:Global JNI handle representing the class object.
/** * A convenience method to return a global handle to a Java class object. The handle will be * tracked and freed with a call to {@link #destroy}. The reference must not be freed manually. * * The class must exist. If not found, the VM terminates. * * @param env JNI environment of the thread running the JVMTI callback. * @param className Java VM type signature of the class. * @return Global JNI handle representing the class object. * * @see #newTrackedGlobalRef */
public JNIObjectHandle newClassGlobalRef(JNIEnvironment env, String className) { assert !destroyed; return newTrackedGlobalRef(env, findClass(env, className)); }
Returns a JNI method ID of a Java method. The method must exist. If not found, the VM terminates.
Params:
  • env – JNI environment of the thread running the JVMTI callback.
  • clazz – Handle to the class containing the method.
  • name – Name of the method.
  • signature – Signature of the method. See the JNI specification for more details.
  • isStatic – Specifies whether the method is static or not.
Returns:JNI method ID of the Java method.
/** * Returns a JNI method ID of a Java method. * * The method must exist. If not found, the VM terminates. * * @param env JNI environment of the thread running the JVMTI callback. * @param clazz Handle to the class containing the method. * @param name Name of the method. * @param signature Signature of the method. See the JNI specification for more details. * @param isStatic Specifies whether the method is static or not. * @return JNI method ID of the Java method. */
public JNIMethodId getMethodId(JNIEnvironment env, JNIObjectHandle clazz, String name, String signature, boolean isStatic) { assert !destroyed; JNIMethodId id = getMethodIdOptional(env, clazz, name, signature, isStatic); guarantee(id.isNonNull()); return id; }
Returns a JNI method ID of a Java method. If the method is not found, returns a JNIObjectHandles.nullHandle().
Params:
  • env – JNI environment of the thread running the JVMTI callback.
  • clazz – Handle to the class containing the method.
  • name – Name of the method.
  • signature – Signature of the method. See the JNI specification for more details.
  • isStatic – Specifies whether the method is static or not.
Returns:JNI method ID of the Java method if found, JNIObjectHandles.nullHandle otherwise.
/** * Returns a JNI method ID of a Java method. If the method is not found, returns a * {@link JNIObjectHandles#nullHandle()}. * * @param env JNI environment of the thread running the JVMTI callback. * @param clazz Handle to the class containing the method. * @param name Name of the method. * @param signature Signature of the method. See the JNI specification for more details. * @param isStatic Specifies whether the method is static or not. * @return JNI method ID of the Java method if found, {@link JNIObjectHandles#nullHandle} * otherwise. */
public JNIMethodId getMethodIdOptional(JNIEnvironment env, JNIObjectHandle clazz, String name, String signature, boolean isStatic) { assert !destroyed; try (CTypeConversion.CCharPointerHolder cname = Support.toCString(name); CTypeConversion.CCharPointerHolder csignature = Support.toCString(signature)) { if (isStatic) { return Support.jniFunctions().getGetStaticMethodID().invoke(env, clazz, cname.get(), csignature.get()); } else { return Support.jniFunctions().getGetMethodID().invoke(env, clazz, cname.get(), csignature.get()); } } } public JNIFieldId getFieldId(JNIEnvironment env, JNIObjectHandle clazz, String name, String signature, boolean isStatic) { assert !destroyed; JNIFieldId id = getFieldIdOptional(env, clazz, name, signature, isStatic); guarantee(id.isNonNull()); return id; } public JNIFieldId getFieldIdOptional(JNIEnvironment env, JNIObjectHandle clazz, String name, String signature, boolean isStatic) { assert !destroyed; try (CTypeConversion.CCharPointerHolder cname = Support.toCString(name); CTypeConversion.CCharPointerHolder csignature = Support.toCString(signature)) { if (isStatic) { return Support.jniFunctions().getGetStaticFieldID().invoke(env, clazz, cname.get(), csignature.get()); } else { return Support.jniFunctions().getGetFieldID().invoke(env, clazz, cname.get(), csignature.get()); } } }
Creates a global JNI handle of the specified local JNI handle. The handle will be tracked and freed with a call to destroy. The handle must not be freed manually. If the specified handle is a JNIObjectHandles.nullHandle the VM terminates.
Params:
  • env – JNI environment of the thread running the JVMTI callback.
  • ref – A local JNI object handle.
Returns:A global JNI object handle.
/** * Creates a global JNI handle of the specified local JNI handle. The handle will be tracked and * freed with a call to {@link #destroy}. The handle must not be freed manually. * * If the specified handle is a {@link JNIObjectHandles#nullHandle} the VM terminates. * * @param env JNI environment of the thread running the JVMTI callback. * @param ref A local JNI object handle. * @return A global JNI object handle. */
public JNIObjectHandle newTrackedGlobalRef(JNIEnvironment env, JNIObjectHandle ref) { assert !destroyed; JNIObjectHandle global = Support.jniFunctions().getNewGlobalRef().invoke(env, ref); guarantee(global.notEqual(nullHandle())); globalRefsLock.lock(); try { if (globalRefCount == globalRefs.length) { globalRefs = Arrays.copyOf(globalRefs, globalRefs.length * 2); } globalRefs[globalRefCount] = global; globalRefCount++; } finally { globalRefsLock.unlock(); } return global; }
Releases all global JNI handles. This class and the handles must not be used after a call to this method. This function is automatically called by JvmtiAgentBase after the agent unloads. It should not be called manually.
Params:
  • env – JNI environment of the thread running the JVMTI callback.
/** * Releases all global JNI handles. This class and the handles must not be used after a call to * this method. This function is automatically called by {@link JvmtiAgentBase} after the agent * unloads. It should not be called manually. * * @param env JNI environment of the thread running the JVMTI callback. */
void destroy(JNIEnvironment env) { assert !destroyed; destroyed = true; for (int i = 0; i < globalRefCount; i++) { Support.jniFunctions().getDeleteGlobalRef().invoke(env, globalRefs[i]); } } }