package com.oracle.svm.jvmtiagentbase;
import static com.oracle.svm.core.util.VMError.guarantee;
import static com.oracle.svm.jni.JNIObjectHandles.nullHandle;
import static org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder;
import static org.graalvm.word.WordFactory.nullPointer;
import java.nio.charset.StandardCharsets;
import org.graalvm.nativeimage.StackValue;
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.WordPointer;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.jni.nativeapi.JNIEnvironment;
import com.oracle.svm.jni.nativeapi.JNIErrors;
import com.oracle.svm.jni.nativeapi.JNIFieldId;
import com.oracle.svm.jni.nativeapi.JNIMethodId;
import com.oracle.svm.jni.nativeapi.JNINativeInterface;
import com.oracle.svm.jni.nativeapi.JNIObjectHandle;
import com.oracle.svm.jni.nativeapi.JNIValue;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEnv;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiError;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiFrameInfo;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiInterface;
public final class Support {
public static boolean isInitialized() {
boolean initialized = jvmtiEnv.isNonNull();
assert initialized == jniFunctions.isNonNull();
return initialized;
}
public static void initialize(JvmtiEnv jvmti) {
VMError.guarantee(!isInitialized());
WordPointer functionsPtr = StackValue.get(WordPointer.class);
check(jvmti.getFunctions().GetJNIFunctionTable().invoke(jvmti, functionsPtr));
guarantee(functionsPtr.read() != nullPointer(), "Functions table must be initialized exactly once");
jvmtiEnv = jvmti;
jniFunctions = functionsPtr.read();
}
public static void destroy() {
jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), jniFunctions);
jniFunctions = nullPointer();
jvmtiEnv = nullPointer();
}
public static String getSystemProperty(JvmtiEnv jvmti, String propertyName) {
try (CCharPointerHolder propertyKey = toCString(propertyName)) {
String propertyValue = null;
CCharPointerPointer propertyValuePtr = StackValue.get(CCharPointerPointer.class);
if (jvmti.getFunctions().GetSystemProperty().invoke(jvmti, propertyKey.get(), propertyValuePtr) == JvmtiError.JVMTI_ERROR_NONE) {
propertyValue = fromCString(propertyValuePtr.read());
check(jvmti.getFunctions().Deallocate().invoke(jvmti, propertyValuePtr.read()));
}
return propertyValue;
}
}
public static String[] getSystemProperties(JvmtiEnv jvmti) {
CIntPointer countPtr = StackValue.get(CIntPointer.class);
WordPointer propertyPtr = StackValue.get(WordPointer.class);
check(jvmti.getFunctions().GetSystemProperties().invoke(jvmti, countPtr, propertyPtr));
int numEntries = countPtr.read();
CCharPointerPointer properties = propertyPtr.read();
String[] result = new String[numEntries];
for (int i = 0; i < numEntries; i++) {
CCharPointer rawEntry = properties.read(i);
result[i] = fromCString(rawEntry);
check(jvmti.getFunctions().Deallocate().invoke(jvmti, rawEntry));
}
check(jvmti.getFunctions().Deallocate().invoke(jvmti, properties));
return result;
}
private static JvmtiEnv jvmtiEnv;
private static JNINativeInterface jniFunctions;
public static JvmtiEnv jvmtiEnv() {
return jvmtiEnv;
}
public static JvmtiInterface jvmtiFunctions() {
return jvmtiEnv.getFunctions();
}
public static JNINativeInterface jniFunctions() {
return jniFunctions;
}
public static String fromCString(CCharPointer s) {
return CTypeConversion.toJavaString(s, SubstrateUtil.strlen(s), StandardCharsets.UTF_8);
}
public static String fromJniString(JNIEnvironment env, JNIObjectHandle handle) {
if (handle.notEqual(nullHandle())) {
CCharPointer cstr = jniFunctions().getGetStringUTFChars().invoke(env, handle, nullPointer());
if (cstr.isNonNull()) {
try {
return fromCString(cstr);
} finally {
jniFunctions().getReleaseStringUTFChars().invoke(env, handle, cstr);
}
}
}
return null;
}
public static JNIObjectHandle toJniString(JNIEnvironment jni, String string) {
try (CCharPointerHolder cString = toCString(string)) {
return jniFunctions().getNewStringUTF().invoke(jni, cString.get());
}
}
public static CCharPointerHolder toCString(String s) {
return CTypeConversion.toCString(s);
}
public static JNIObjectHandle getCallerClass(int depth) {
return getMethodDeclaringClass(getCallerMethod(depth));
}
public static JNIObjectHandle getDirectCallerClass() {
return getCallerClass(1);
}
public static JNIMethodId getCallerMethod(int depth) {
JvmtiFrameInfo frameInfo = StackValue.get(JvmtiFrameInfo.class);
CIntPointer countPtr = StackValue.get(CIntPointer.class);
JvmtiError result = jvmtiFunctions().GetStackTrace().invoke(jvmtiEnv(), nullHandle(), depth, 1, (WordPointer) frameInfo, countPtr);
if (result == JvmtiError.JVMTI_ERROR_NONE && countPtr.read() == 1) {
return frameInfo.getMethod();
}
return nullPointer();
}
public static JNIObjectHandle getObjectArgument(int slot) {
WordPointer handlePtr = StackValue.get(WordPointer.class);
if (jvmtiFunctions().GetLocalObject().invoke(jvmtiEnv(), nullHandle(), 0, slot, handlePtr) != JvmtiError.JVMTI_ERROR_NONE) {
return nullHandle();
}
return handlePtr.read();
}
public static String getClassNameOr(JNIEnvironment env, JNIObjectHandle clazz, String forNullHandle, String forNullNameOrException) {
if (clazz.notEqual(nullHandle())) {
JNIObjectHandle clazzName = callObjectMethod(env, clazz, JvmtiAgentBase.singleton().handles().javaLangClassGetName);
String result = Support.fromJniString(env, clazzName);
if (result == null || clearException(env)) {
result = forNullNameOrException;
}
return result;
}
return forNullHandle;
}
public static String getClassNameOrNull(JNIEnvironment env, JNIObjectHandle clazz) {
return getClassNameOr(env, clazz, null, null);
}
public static JNIObjectHandle getMethodDeclaringClass(JNIMethodId method) {
WordPointer declaringClass = StackValue.get(WordPointer.class);
if (method.isNull() || jvmtiFunctions().GetMethodDeclaringClass().invoke(jvmtiEnv(), method, declaringClass) != JvmtiError.JVMTI_ERROR_NONE) {
declaringClass.write(nullPointer());
}
return declaringClass.read();
}
public static JNIObjectHandle getFieldDeclaringClass(JNIObjectHandle clazz, JNIFieldId method) {
WordPointer declaringClass = StackValue.get(WordPointer.class);
if (method.isNull() || jvmtiFunctions().GetFieldDeclaringClass().invoke(jvmtiEnv(), clazz, method, declaringClass) != JvmtiError.JVMTI_ERROR_NONE) {
declaringClass.write(nullPointer());
}
return declaringClass.read();
}
public static String getFieldName(JNIObjectHandle clazz, JNIFieldId field) {
String name = null;
CCharPointerPointer namePtr = StackValue.get(CCharPointerPointer.class);
if (jvmtiFunctions().GetFieldName().invoke(jvmtiEnv(), clazz, field, namePtr, nullPointer(), nullPointer()) == JvmtiError.JVMTI_ERROR_NONE) {
name = fromCString(namePtr.read());
jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), namePtr.read());
}
return name;
}
public static String getMethodNameOr(JNIMethodId methodId, String defaultValue) {
String methodName = defaultValue;
CCharPointerPointer methodNamePtr = StackValue.get(CCharPointerPointer.class);
if (jvmtiFunctions().GetMethodName().invoke(jvmtiEnv(), methodId, methodNamePtr, nullHandle(), nullHandle()) == JvmtiError.JVMTI_ERROR_NONE) {
methodName = Support.fromCString(methodNamePtr.read());
jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), methodNamePtr.read());
}
return methodName;
}
public static JNIObjectHandle getObjectField(JNIEnvironment env, JNIObjectHandle clazz, JNIObjectHandle obj, String name, String signature) {
try (CCharPointerHolder nameHolder = toCString(name);
CCharPointerHolder sigHolder = toCString(signature);) {
JNIFieldId fieldId = jniFunctions().getGetFieldID().invoke(env, clazz, nameHolder.get(), sigHolder.get());
if (nullHandle().notEqual(fieldId)) {
return jniFunctions().getGetObjectField().invoke(env, obj, fieldId);
} else {
return nullHandle();
}
}
}
public static boolean clearException(JNIEnvironment localEnv) {
if (jniFunctions().getExceptionCheck().invoke(localEnv)) {
jniFunctions().getExceptionClear().invoke(localEnv);
return true;
}
return false;
}
public static boolean testException(JNIEnvironment localEnv) {
if (jniFunctions().getExceptionCheck().invoke(localEnv)) {
jniFunctions().getExceptionDescribe().invoke(localEnv);
return true;
}
return false;
}
public static JNIObjectHandle callObjectMethod(JNIEnvironment env, JNIObjectHandle obj, JNIMethodId method) {
return jniFunctions().getCallObjectMethodA().invoke(env, obj, method, nullPointer());
}
public static JNIObjectHandle callObjectMethodL(JNIEnvironment env, JNIObjectHandle obj, JNIMethodId method, JNIObjectHandle l0) {
JNIValue args = StackValue.get(1, JNIValue.class);
args.setObject(l0);
return jniFunctions().getCallObjectMethodA().invoke(env, obj, method, args);
}
public static JNIObjectHandle callObjectMethodLL(JNIEnvironment env, JNIObjectHandle obj, JNIMethodId method, JNIObjectHandle l0, JNIObjectHandle l1) {
JNIValue args = StackValue.get(2, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
return jniFunctions().getCallObjectMethodA().invoke(env, obj, method, args);
}
public static JNIObjectHandle callObjectMethodLLL(JNIEnvironment env, JNIObjectHandle obj, JNIMethodId method, JNIObjectHandle l0, JNIObjectHandle l1, JNIObjectHandle l2) {
JNIValue args = StackValue.get(3, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
args.addressOf(2).setObject(l2);
return jniFunctions().getCallObjectMethodA().invoke(env, obj, method, args);
}
public static JNIObjectHandle callObjectMethodLLLL(JNIEnvironment env, JNIObjectHandle obj, JNIMethodId method, JNIObjectHandle l0, JNIObjectHandle l1, JNIObjectHandle l2, JNIObjectHandle l3) {
JNIValue args = StackValue.get(4, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
args.addressOf(2).setObject(l2);
args.addressOf(3).setObject(l3);
return jniFunctions().getCallObjectMethodA().invoke(env, obj, method, args);
}
public static JNIObjectHandle callStaticObjectMethodL(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId method, JNIObjectHandle l0) {
JNIValue args = StackValue.get(1, JNIValue.class);
args.setObject(l0);
return jniFunctions().getCallStaticObjectMethodA().invoke(env, clazz, method, args);
}
public static JNIObjectHandle callStaticObjectMethodLL(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId method, JNIObjectHandle l0, JNIObjectHandle l1) {
JNIValue args = StackValue.get(2, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
return jniFunctions().getCallStaticObjectMethodA().invoke(env, clazz, method, args);
}
public static JNIObjectHandle callStaticObjectMethodLIL(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId method, JNIObjectHandle l0, int i1, JNIObjectHandle l2) {
JNIValue args = StackValue.get(3, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setInt(i1);
args.addressOf(2).setObject(l2);
return jniFunctions().getCallStaticObjectMethodA().invoke(env, clazz, method, args);
}
public static JNIObjectHandle callStaticObjectMethodLLL(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId method, JNIObjectHandle l0, JNIObjectHandle l1, JNIObjectHandle l2) {
JNIValue args = StackValue.get(3, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
args.addressOf(2).setObject(l2);
return jniFunctions().getCallStaticObjectMethodA().invoke(env, clazz, method, args);
}
public static JNIObjectHandle callStaticObjectMethodLLLL(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId method,
JNIObjectHandle l0, JNIObjectHandle l1, JNIObjectHandle l2, JNIObjectHandle l3) {
JNIValue args = StackValue.get(4, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
args.addressOf(2).setObject(l2);
args.addressOf(3).setObject(l3);
return jniFunctions().getCallStaticObjectMethodA().invoke(env, clazz, method, args);
}
public static JNIObjectHandle callStaticObjectMethodLLLLL(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId method,
JNIObjectHandle l0, JNIObjectHandle l1, JNIObjectHandle l2, JNIObjectHandle l3, JNIObjectHandle l4) {
JNIValue args = StackValue.get(5, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
args.addressOf(2).setObject(l2);
args.addressOf(3).setObject(l3);
args.addressOf(4).setObject(l4);
return jniFunctions().getCallStaticObjectMethodA().invoke(env, clazz, method, args);
}
public static void callStaticVoidMethodLL(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId method, JNIObjectHandle l0, JNIObjectHandle l1) {
JNIValue args = StackValue.get(2, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
jniFunctions().getCallStaticVoidMethodA().invoke(env, clazz, method, args);
}
public static boolean callBooleanMethod(JNIEnvironment env, JNIObjectHandle obj, JNIMethodId method) {
return jniFunctions().getCallBooleanMethodA().invoke(env, obj, method, nullPointer());
}
public static long callLongMethodL(JNIEnvironment env, JNIObjectHandle obj, JNIMethodId method, JNIObjectHandle l0) {
JNIValue args = StackValue.get(1, JNIValue.class);
args.addressOf(0).setObject(l0);
return jniFunctions().getCallLongMethodA().invoke(env, obj, method, args);
}
public static long callLongMethodLL(JNIEnvironment env, JNIObjectHandle obj, JNIMethodId method, JNIObjectHandle l0, JNIObjectHandle l1) {
JNIValue args = StackValue.get(2, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
return jniFunctions().getCallLongMethodA().invoke(env, obj, method, args);
}
public static int callIntMethodL(JNIEnvironment env, JNIObjectHandle obj, JNIMethodId method, JNIObjectHandle l0) {
JNIValue args = StackValue.get(1, JNIValue.class);
args.addressOf(0).setObject(l0);
return jniFunctions().getCallIntMethodA().invoke(env, obj, method, args);
}
public static JNIObjectHandle newObjectL(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId ctor, JNIObjectHandle l0) {
JNIValue args = StackValue.get(1, JNIValue.class);
args.addressOf(0).setObject(l0);
return jniFunctions().getNewObjectA().invoke(env, clazz, ctor, args);
}
public static JNIObjectHandle newObjectLLLJ(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId ctor, JNIObjectHandle l0, JNIObjectHandle l1, JNIObjectHandle l2, long l3) {
JNIValue args = StackValue.get(4, JNIValue.class);
args.addressOf(0).setObject(l0);
args.addressOf(1).setObject(l1);
args.addressOf(2).setObject(l2);
args.addressOf(3).setLong(l3);
return jniFunctions().getNewObjectA().invoke(env, clazz, ctor, args);
}
public static void checkNoException(JNIEnvironment localEnv) {
VMError.guarantee(!testException(localEnv));
}
public static void check(JvmtiError resultCode) {
guarantee(resultCode.equals(JvmtiError.JVMTI_ERROR_NONE), "JVMTI call failed with " + resultCode.name());
}
public static void checkJni(int resultCode) {
guarantee(resultCode == JNIErrors.JNI_OK());
}
private Support() {
}
}