using LibRyujinx.Jni.Identifiers; using LibRyujinx.Jni.Pointers; using LibRyujinx.Jni.Primitives; using LibRyujinx.Jni.References; using LibRyujinx.Jni.Values; using System; using Rxmxnx.PInvoke; namespace LibRyujinx.Jni { internal static class JniHelper { public const Int32 JniVersion = 0x00010006; //JNI_VERSION_1_6; public static JEnvRef? Attach(JavaVMRef javaVm, IReadOnlyFixedMemory threadName, out Boolean newAttach) { ref JavaVMValue value = ref javaVm.VirtualMachine; ref JInvokeInterface jInvoke = ref value.Functions; IntPtr getEnvPtr = jInvoke.GetEnvPointer; GetEnvDelegate getEnv = getEnvPtr.GetUnsafeDelegate()!; if (getEnv(javaVm, out JEnvRef jEnv, JniHelper.JniVersion) == JResult.Ok) { newAttach = false; return jEnv; } JavaVMAttachArgs args = new() { Version = JniHelper.JniVersion, Name = threadName.ValuePointer, }; IntPtr attachCurrentThreadPtr = jInvoke.AttachCurrentThreadPointer; AttachCurrentThreadDelegate attachCurrentThread = attachCurrentThreadPtr.GetUnsafeDelegate()!; newAttach = true; return attachCurrentThread(javaVm, out jEnv, in args) == JResult.Ok ? jEnv : null; } public static JEnvRef? AttachDaemon(JavaVMRef javaVm, IReadOnlyFixedMemory daemonName) { ref JavaVMValue value = ref javaVm.VirtualMachine; ref JInvokeInterface jInvoke = ref value.Functions; JavaVMAttachArgs args = new() { Version = JniHelper.JniVersion, Name = daemonName.ValuePointer, }; IntPtr attachCurrentThreadAsDaemonPtr = jInvoke.AttachCurrentThreadAsDaemonPointer; AttachCurrentThreadAsDaemonDelegate attachCurrentThreadAsDaemon = attachCurrentThreadAsDaemonPtr.GetUnsafeDelegate()!; return attachCurrentThreadAsDaemon(javaVm, out JEnvRef jEnv, in args) == JResult.Ok ? jEnv : null; } public static void Detach(JavaVMRef javaVm) { ref JavaVMValue value = ref javaVm.VirtualMachine; ref JInvokeInterface jInvoke = ref value.Functions; IntPtr detachCurrentThreadPtr = jInvoke.DetachCurrentThreadPointer; DetachCurrentThreadDelegate detachCurrentThread = detachCurrentThreadPtr.GetUnsafeDelegate()!; detachCurrentThread(javaVm); } public static JGlobalRef? GetGlobalClass(JEnvRef jEnv, IReadOnlyFixedMemory className) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr findClassPtr = jInterface.FindClassPointer; FindClassDelegate findClass = findClassPtr.GetUnsafeDelegate()!; JClassLocalRef jClass = findClass(jEnv, className.ValuePointer); if (JniHelper.ExceptionCheck(jEnv)) return default; IntPtr newGlobalRefPtr = jInterface.NewGlobalRefPointer; NewGlobalRefDelegate newGlobalRef = newGlobalRefPtr.GetUnsafeDelegate()!; JGlobalRef jGlobal = newGlobalRef(jEnv, (JObjectLocalRef)jClass); JniHelper.RemoveLocal(jEnv, (JObjectLocalRef)jClass); return !JniHelper.ExceptionCheck(jEnv) ? jGlobal : null; } public static void RemoveLocal(JEnvRef jEnv, JObjectLocalRef jObject) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr deleteLocalRefPtr = jInterface.DeleteLocalRefPointer; DeleteLocalRefDelegate deleteLocalRef = deleteLocalRefPtr.GetUnsafeDelegate()!; deleteLocalRef(jEnv, jObject); } public static void RemoveGlobal(JEnvRef jEnv, JGlobalRef jObject) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr deleteGlobalRefPtr = jInterface.DeleteGlobalRefPointer; DeleteGlobalRefDelegate deleteGlobalRef = deleteGlobalRefPtr.GetUnsafeDelegate()!; deleteGlobalRef(jEnv, jObject); } public static void RemoveWeakGlobal(JEnvRef jEnv, JWeakRef jObject) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr deleteWeakGlobalRefPtr = jInterface.DeleteWeakGlobalRefPointer; DeleteWeakGlobalRefDelegate deleteWeakGlobalRef = deleteWeakGlobalRefPtr.GetUnsafeDelegate()!; deleteWeakGlobalRef(jEnv, jObject); } public static JStringLocalRef? CreateString(JEnvRef jEnv, String textValue) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr newStringPtr = jInterface.NewStringPointer; NewStringDelegate newString = newStringPtr.GetUnsafeDelegate()!; using IReadOnlyFixedMemory.IDisposable ctx = textValue.AsMemory().GetFixedContext(); JStringLocalRef jString = newString(jEnv, ctx.ValuePointer, ctx.Values.Length); return !JniHelper.ExceptionCheck(jEnv) ? jString : null; } public static JWeakRef? CreateWeakGlobal(JEnvRef jEnv, JObjectLocalRef jObject) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr newWeakGlobalRefPtr = jInterface.NewWeakGlobalRefPointer; NewWeakGlobalRefDelegate newWeakGlobalRef = newWeakGlobalRefPtr.GetUnsafeDelegate()!; JWeakRef jWeak = newWeakGlobalRef(jEnv, jObject); return !JniHelper.ExceptionCheck(jEnv) ? jWeak : null; } private static Boolean ExceptionCheck(JEnvRef jEnv) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr exceptionCheckPtr = jInterface.ExceptionCheckPointer; ExceptionCheckDelegate exceptionCheck = exceptionCheckPtr.GetUnsafeDelegate()!; if (!exceptionCheck(jEnv)) return false; IntPtr exceptionDescribePtr = jInterface.ExceptionDescribePointer; IntPtr exceptionClearPtr = jInterface.ExceptionClearPointer; ExceptionDescribeDelegate exceptionDescribe = exceptionDescribePtr.GetUnsafeDelegate()!; ExceptionClearDelegate exceptionClear = exceptionClearPtr.GetUnsafeDelegate()!; exceptionDescribe(jEnv); exceptionClear(jEnv); return true; } public static JavaVMRef? GetVirtualMachine(JEnvRef jEnv) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr getJavaVmPtr = jInterface.GetJavaVMPointer; GetVirtualMachineDelegate getJavaVm = getJavaVmPtr.GetUnsafeDelegate()!; return getJavaVm(jEnv, out JavaVMRef javaVm) == JResult.Ok ? javaVm : null; } public static Boolean? IsValidGlobalWeak(JEnvRef jEnv, JWeakRef jWeak) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr isSameObjectPtr = jInterface.IsSameObjectPointer; IsSameObjectDelegate isSameObject = isSameObjectPtr.GetUnsafeDelegate()!; JBoolean result = isSameObject(jEnv, (JObjectLocalRef)jWeak, default); return !JniHelper.ExceptionCheck(jEnv) ? !result : null; } public static JMethodId? GetMethodId(JEnvRef jEnv, JClassLocalRef jClass, IReadOnlyFixedMemory methodName, IReadOnlyFixedMemory descriptor) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr getMethodIdPtr = jInterface.GetMethodIdPointer; GetMethodIdDelegate getMethodId = getMethodIdPtr.GetUnsafeDelegate()!; JMethodId methodId = getMethodId(jEnv, jClass, methodName.ValuePointer, descriptor.ValuePointer); return !JniHelper.ExceptionCheck(jEnv) ? methodId : null; } public static JMethodId? GetStaticMethodId(JEnvRef jEnv, JClassLocalRef jClass, IReadOnlyFixedMemory methodName, IReadOnlyFixedMemory descriptor) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr getStaticMethodIdPtr = jInterface.GetStaticMethodIdPointer; GetStaticMethodIdDelegate getStaticMethodId = getStaticMethodIdPtr.GetUnsafeDelegate()!; JMethodId jMethodId = getStaticMethodId(jEnv, jClass, methodName.ValuePointer, descriptor.ValuePointer); return !JniHelper.ExceptionCheck(jEnv) ? jMethodId : null; } public static void CallStaticVoidMethod(JEnvRef jEnv, JClassLocalRef jClass, JMethodId jMethodId, params JValue[] args) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr callStaticVoidMethodPtr = jInterface.CallStaticVoidMethodAPointer; CallStaticVoidMethodADelegate callStaticVoidMethod = callStaticVoidMethodPtr.GetUnsafeDelegate()!; using IReadOnlyFixedMemory.IDisposable fArgs = args.AsMemory().GetFixedContext(); callStaticVoidMethod(jEnv, jClass, jMethodId, fArgs.ValuePointer); } public static JObjectLocalRef? CallStaticObjectMethod(JEnvRef jEnv, JClassLocalRef jClass, JMethodId jMethodId, params JValue[] args) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr callStaticObjectMethodPtr = jInterface.CallStaticObjectMethodAPointer; CallStaticObjectMethodADelegate callStaticObjectMethod = callStaticObjectMethodPtr.GetUnsafeDelegate()!; using IReadOnlyFixedMemory.IDisposable fArgs = args.AsMemory().GetFixedContext(); JObjectLocalRef jObject = callStaticObjectMethod(jEnv, jClass, jMethodId, fArgs.ValuePointer); return !JniHelper.ExceptionCheck(jEnv) ? jObject : null; } public static JLong? CallStaticLongMethod(JEnvRef jEnv, JClassLocalRef jClass, JMethodId jMethodId, params JValue[] args) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr callStaticLongMethodPtr = jInterface.CallStaticLongMethodAPointer; CallStaticLongMethodADelegate callStaticLongMethod = callStaticLongMethodPtr.GetUnsafeDelegate()!; using IReadOnlyFixedMemory.IDisposable fArgs = args.AsMemory().GetFixedContext(); JLong jLong = callStaticLongMethod(jEnv, jClass, jMethodId, fArgs.ValuePointer); return !JniHelper.ExceptionCheck(jEnv) ? jLong : null; } public static void CallVoidMethod(JEnvRef jEnv, JObjectLocalRef jObject, JMethodId jMethodId, params JValue[] args) { ref readonly JEnvValue value = ref jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr callVoidMethodPtr = jInterface.CallVoidMethodAPointer; CallVoidMethodADelegate callVoidMethod = callVoidMethodPtr.GetUnsafeDelegate()!; using IReadOnlyFixedMemory.IDisposable fArgs = args.AsMemory().GetFixedContext(); callVoidMethod(jEnv, jObject, jMethodId, fArgs.ValuePointer); } } }