blob: 382c9bc43cb56cdb7caf198a29c89cd5709df48e [file] [log] [blame]
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include <jni.h>
#include <stdarg.h>
#include <stdint.h>
#include "dartjni.h"
#include "include/dart_api_dl.h"
void initAllLocks(JniLocks* locks) {
init_lock(&locks->classLoadingLock);
}
/// Load class through platform-specific mechanism.
///
/// Currently uses application classloader on android,
/// and JNIEnv->FindClass on other platforms.
jclass FindClass(const char* name) {
attach_thread();
jclass cls;
load_class_platform(&cls, name);
if (!(*jniEnv)->ExceptionCheck(jniEnv)) {
cls = to_global_ref(cls);
}
return cls;
};
/// Stores class and method references for obtaining exception details
typedef struct JniExceptionMethods {
jclass objectClass, exceptionClass, printStreamClass;
jclass byteArrayOutputStreamClass;
jmethodID toStringMethod, printStackTraceMethod;
jmethodID byteArrayOutputStreamCtor, printStreamCtor;
} JniExceptionMethods;
// Context and shared global state. Initialized once or if thread-local, initialized once in a thread.
JniContext jni_context = {
.jvm = NULL,
.classLoader = NULL,
.loadClassMethod = NULL,
.appContext = NULL,
.currentActivity = NULL,
};
JniContext* jni = &jni_context;
thread_local JNIEnv* jniEnv = NULL;
JniExceptionMethods exceptionMethods;
void initExceptionHandling(JniExceptionMethods* methods) {
methods->objectClass = FindClass("java/lang/Object");
methods->exceptionClass = FindClass("java/lang/Exception");
methods->printStreamClass = FindClass("java/io/PrintStream");
methods->byteArrayOutputStreamClass =
FindClass("java/io/ByteArrayOutputStream");
load_method(methods->objectClass, &methods->toStringMethod, "toString",
"()Ljava/lang/String;");
load_method(methods->exceptionClass, &methods->printStackTraceMethod,
"printStackTrace", "(Ljava/io/PrintStream;)V");
load_method(methods->byteArrayOutputStreamClass,
&methods->byteArrayOutputStreamCtor, "<init>", "()V");
load_method(methods->printStreamClass, &methods->printStreamCtor, "<init>",
"(Ljava/io/OutputStream;)V");
}
/// Get JVM associated with current process.
/// Returns NULL if no JVM is running.
FFI_PLUGIN_EXPORT
JavaVM* GetJavaVM() {
return jni_context.jvm;
}
// Android specifics
FFI_PLUGIN_EXPORT
jobject GetClassLoader() {
attach_thread();
return (*jniEnv)->NewGlobalRef(jniEnv, jni_context.classLoader);
}
FFI_PLUGIN_EXPORT
jobject GetApplicationContext() {
attach_thread();
return (*jniEnv)->NewGlobalRef(jniEnv, jni_context.appContext);
}
FFI_PLUGIN_EXPORT
jobject GetCurrentActivity() {
attach_thread();
return (*jniEnv)->NewGlobalRef(jniEnv, jni_context.currentActivity);
}
// JNI Initialization
#ifdef __ANDROID__
JNIEXPORT void JNICALL
Java_com_github_dart_1lang_jni_JniPlugin_initializeJni(JNIEnv* env,
jobject obj,
jobject appContext,
jobject classLoader) {
jniEnv = env;
(*env)->GetJavaVM(env, &jni_context.jvm);
jni_context.classLoader = (*env)->NewGlobalRef(env, classLoader);
jni_context.appContext = (*env)->NewGlobalRef(env, appContext);
jclass classLoaderClass = (*env)->GetObjectClass(env, classLoader);
jni_context.loadClassMethod =
(*env)->GetMethodID(env, classLoaderClass, "loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
initAllLocks(&jni_context.locks);
initExceptionHandling(&exceptionMethods);
}
JNIEXPORT void JNICALL
Java_com_github_dart_1lang_jni_JniPlugin_setJniActivity(JNIEnv* env,
jobject obj,
jobject activity,
jobject context) {
jniEnv = env;
if (jni_context.currentActivity != NULL) {
(*env)->DeleteGlobalRef(env, jni_context.currentActivity);
}
jni_context.currentActivity = (*env)->NewGlobalRef(env, activity);
if (jni_context.appContext != NULL) {
(*env)->DeleteGlobalRef(env, jni_context.appContext);
}
jni_context.appContext = (*env)->NewGlobalRef(env, context);
}
// Sometimes you may get linker error trying to link JNI_CreateJavaVM APIs
// on Android NDK. So IFDEF is required.
#else
#ifdef _WIN32
// Pre-initialization of critical section on windows - this is required because
// there's no coordination between multiple isolates calling Spawn.
//
// Taken from https://stackoverflow.com/a/12858955
CRITICAL_SECTION spawnLock = {0};
BOOL WINAPI DllMain(HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpReserved) { // reserved
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
InitializeCriticalSection(&spawnLock);
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
DeleteCriticalSection(&spawnLock);
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
#else
pthread_mutex_t spawnLock = PTHREAD_MUTEX_INITIALIZER;
#endif
FFI_PLUGIN_EXPORT
int SpawnJvm(JavaVMInitArgs* initArgs) {
if (jni_context.jvm != NULL) {
return DART_JNI_SINGLETON_EXISTS;
}
acquire_lock(&spawnLock);
// Init may have happened in the meanwhile.
if (jni_context.jvm != NULL) {
release_lock(&spawnLock);
return DART_JNI_SINGLETON_EXISTS;
}
JavaVMOption jvmopt[1];
char class_path[] = "-Djava.class.path=.";
jvmopt[0].optionString = class_path;
JavaVMInitArgs vmArgs;
if (!initArgs) {
vmArgs.version = JNI_VERSION_1_6;
vmArgs.nOptions = 1;
vmArgs.options = jvmopt;
vmArgs.ignoreUnrecognized = JNI_TRUE;
initArgs = &vmArgs;
}
const long flag =
JNI_CreateJavaVM(&jni_context.jvm, __ENVP_CAST & jniEnv, initArgs);
if (flag != JNI_OK) {
return flag;
}
initAllLocks(&jni_context.locks);
initExceptionHandling(&exceptionMethods);
release_lock(&spawnLock);
return JNI_OK;
}
#endif
// accessors - a bunch of functions which are directly called by jnigen generated bindings
// and also package:jni reflective method access.
JniClassLookupResult getClass(char* internalName) {
JniClassLookupResult result = {NULL, NULL};
result.value = FindClass(internalName);
result.exception = check_exception();
return result;
}
typedef void* (*MemberGetter)(JNIEnv* env, jclass clazz, char* name, char* sig);
static inline JniPointerResult _getId(MemberGetter getter,
jclass cls,
char* name,
char* sig) {
JniPointerResult result = {NULL, NULL};
result.value = getter(jniEnv, cls, name, sig);
result.exception = check_exception();
return result;
}
JniPointerResult getMethodID(jclass cls, char* name, char* sig) {
attach_thread();
return _getId((MemberGetter)(*jniEnv)->GetMethodID, cls, name, sig);
}
JniPointerResult getStaticMethodID(jclass cls, char* name, char* sig) {
attach_thread();
return _getId((MemberGetter)(*jniEnv)->GetStaticMethodID, cls, name, sig);
}
JniPointerResult getFieldID(jclass cls, char* name, char* sig) {
attach_thread();
return _getId((MemberGetter)(*jniEnv)->GetFieldID, cls, name, sig);
}
JniPointerResult getStaticFieldID(jclass cls, char* name, char* sig) {
attach_thread();
return _getId((MemberGetter)(*jniEnv)->GetStaticFieldID, cls, name, sig);
}
JniResult callMethod(jobject obj,
jmethodID fieldID,
int callType,
jvalue* args) {
attach_thread();
jvalue result = {.j = 0};
switch (callType) {
case booleanType:
result.z = (*jniEnv)->CallBooleanMethodA(jniEnv, obj, fieldID, args);
break;
case byteType:
result.b = (*jniEnv)->CallByteMethodA(jniEnv, obj, fieldID, args);
break;
case shortType:
result.s = (*jniEnv)->CallShortMethodA(jniEnv, obj, fieldID, args);
break;
case charType:
result.c = (*jniEnv)->CallCharMethodA(jniEnv, obj, fieldID, args);
break;
case intType:
result.i = (*jniEnv)->CallIntMethodA(jniEnv, obj, fieldID, args);
break;
case longType:
result.j = (*jniEnv)->CallLongMethodA(jniEnv, obj, fieldID, args);
break;
case floatType:
result.f = (*jniEnv)->CallFloatMethodA(jniEnv, obj, fieldID, args);
break;
case doubleType:
result.d = (*jniEnv)->CallDoubleMethodA(jniEnv, obj, fieldID, args);
break;
case objectType:
result.l = (*jniEnv)->CallObjectMethodA(jniEnv, obj, fieldID, args);
break;
case voidType:
(*jniEnv)->CallVoidMethodA(jniEnv, obj, fieldID, args);
break;
}
jthrowable exception = check_exception();
if (callType == objectType && exception == NULL) {
result.l = to_global_ref(result.l);
}
JniResult jniResult = {.value = result, .exception = exception};
return jniResult;
}
// TODO(#60): Any way to reduce this boilerplate?
JniResult callStaticMethod(jclass cls,
jmethodID methodID,
int callType,
jvalue* args) {
attach_thread();
jvalue result = {.j = 0};
switch (callType) {
case booleanType:
result.z =
(*jniEnv)->CallStaticBooleanMethodA(jniEnv, cls, methodID, args);
break;
case byteType:
result.b = (*jniEnv)->CallStaticByteMethodA(jniEnv, cls, methodID, args);
break;
case shortType:
result.s = (*jniEnv)->CallStaticShortMethodA(jniEnv, cls, methodID, args);
break;
case charType:
result.c = (*jniEnv)->CallStaticCharMethodA(jniEnv, cls, methodID, args);
break;
case intType:
result.i = (*jniEnv)->CallStaticIntMethodA(jniEnv, cls, methodID, args);
break;
case longType:
result.j = (*jniEnv)->CallStaticLongMethodA(jniEnv, cls, methodID, args);
break;
case floatType:
result.f = (*jniEnv)->CallStaticFloatMethodA(jniEnv, cls, methodID, args);
break;
case doubleType:
result.d =
(*jniEnv)->CallStaticDoubleMethodA(jniEnv, cls, methodID, args);
break;
case objectType:
result.l =
(*jniEnv)->CallStaticObjectMethodA(jniEnv, cls, methodID, args);
break;
case voidType:
(*jniEnv)->CallStaticVoidMethodA(jniEnv, cls, methodID, args);
break;
}
jthrowable exception = check_exception();
if (callType == objectType && exception == NULL) {
result.l = to_global_ref(result.l);
}
JniResult jniResult = {.value = result, .exception = exception};
return jniResult;
}
JniResult getField(jobject obj, jfieldID fieldID, int callType) {
attach_thread();
jvalue result = {.j = 0};
switch (callType) {
case booleanType:
result.z = (*jniEnv)->GetBooleanField(jniEnv, obj, fieldID);
break;
case byteType:
result.b = (*jniEnv)->GetByteField(jniEnv, obj, fieldID);
break;
case shortType:
result.s = (*jniEnv)->GetShortField(jniEnv, obj, fieldID);
break;
case charType:
result.c = (*jniEnv)->GetCharField(jniEnv, obj, fieldID);
break;
case intType:
result.i = (*jniEnv)->GetIntField(jniEnv, obj, fieldID);
break;
case longType:
result.j = (*jniEnv)->GetLongField(jniEnv, obj, fieldID);
break;
case floatType:
result.f = (*jniEnv)->GetFloatField(jniEnv, obj, fieldID);
break;
case doubleType:
result.d = (*jniEnv)->GetDoubleField(jniEnv, obj, fieldID);
break;
case objectType:
result.l = (*jniEnv)->GetObjectField(jniEnv, obj, fieldID);
break;
case voidType:
// This error should have been handled in Dart.
break;
}
jthrowable exception = check_exception();
if (callType == objectType && exception == NULL) {
result.l = to_global_ref(result.l);
}
JniResult jniResult = {.value = result, .exception = exception};
return jniResult;
}
FFI_PLUGIN_EXPORT
JniResult getStaticField(jclass cls, jfieldID fieldID, int callType) {
attach_thread();
jvalue result = {.j = 0};
switch (callType) {
case booleanType:
result.z = (*jniEnv)->GetStaticBooleanField(jniEnv, cls, fieldID);
break;
case byteType:
result.b = (*jniEnv)->GetStaticByteField(jniEnv, cls, fieldID);
break;
case shortType:
result.s = (*jniEnv)->GetStaticShortField(jniEnv, cls, fieldID);
break;
case charType:
result.c = (*jniEnv)->GetStaticCharField(jniEnv, cls, fieldID);
break;
case intType:
result.i = (*jniEnv)->GetStaticIntField(jniEnv, cls, fieldID);
break;
case longType:
result.j = (*jniEnv)->GetStaticLongField(jniEnv, cls, fieldID);
break;
case floatType:
result.f = (*jniEnv)->GetStaticFloatField(jniEnv, cls, fieldID);
break;
case doubleType:
result.d = (*jniEnv)->GetStaticDoubleField(jniEnv, cls, fieldID);
break;
case objectType:
result.l = (*jniEnv)->GetStaticObjectField(jniEnv, cls, fieldID);
break;
case voidType:
// This error should have been handled in dart.
// is there a way to mark this as unreachable?
// or throw exception in Dart using Dart's C API.
break;
}
jthrowable exception = check_exception();
if (callType == objectType && exception == NULL) {
result.l = to_global_ref(result.l);
}
JniResult jniResult = {.value = result, .exception = exception};
return jniResult;
}
JniResult newObject(jclass cls, jmethodID ctor, jvalue* args) {
attach_thread();
jobject result = (*jniEnv)->NewObjectA(jniEnv, cls, ctor, args);
return to_global_ref_result(result);
}
JniResult newPrimitiveArray(jsize length, int type) {
attach_thread();
jarray array;
switch (type) {
case booleanType:
array = (*jniEnv)->NewBooleanArray(jniEnv, length);
break;
case byteType:
array = (*jniEnv)->NewByteArray(jniEnv, length);
break;
case shortType:
array = (*jniEnv)->NewShortArray(jniEnv, length);
break;
case charType:
array = (*jniEnv)->NewCharArray(jniEnv, length);
break;
case intType:
array = (*jniEnv)->NewIntArray(jniEnv, length);
break;
case longType:
array = (*jniEnv)->NewLongArray(jniEnv, length);
break;
case floatType:
array = (*jniEnv)->NewFloatArray(jniEnv, length);
break;
case doubleType:
array = (*jniEnv)->NewDoubleArray(jniEnv, length);
break;
case objectType:
case voidType:
// This error should have been handled in dart.
// is there a way to mark this as unreachable?
// or throw exception in Dart using Dart's C API.
array = NULL;
break;
}
return to_global_ref_result(array);
}
JniResult newObjectArray(jsize length,
jclass elementClass,
jobject initialElement) {
attach_thread();
jarray array =
(*jniEnv)->NewObjectArray(jniEnv, length, elementClass, initialElement);
return to_global_ref_result(array);
}
JniResult getArrayElement(jarray array, int index, int type) {
attach_thread();
jvalue value;
switch (type) {
case booleanType:
(*jniEnv)->GetBooleanArrayRegion(jniEnv, array, index, 1, &value.z);
break;
case byteType:
(*jniEnv)->GetByteArrayRegion(jniEnv, array, index, 1, &value.b);
break;
case shortType:
(*jniEnv)->GetShortArrayRegion(jniEnv, array, index, 1, &value.s);
break;
case charType:
(*jniEnv)->GetCharArrayRegion(jniEnv, array, index, 1, &value.c);
break;
case intType:
(*jniEnv)->GetIntArrayRegion(jniEnv, array, index, 1, &value.i);
break;
case longType:
(*jniEnv)->GetLongArrayRegion(jniEnv, array, index, 1, &value.j);
break;
case floatType:
(*jniEnv)->GetFloatArrayRegion(jniEnv, array, index, 1, &value.f);
break;
case doubleType:
(*jniEnv)->GetDoubleArrayRegion(jniEnv, array, index, 1, &value.d);
break;
case objectType:
value.l = (*jniEnv)->GetObjectArrayElement(jniEnv, array, index);
case voidType:
// This error should have been handled in dart.
// is there a way to mark this as unreachable?
// or throw exception in Dart using Dart's C API.
break;
}
jthrowable exception = check_exception();
if (type == objectType && exception == NULL) {
value.l = to_global_ref(value.l);
}
JniResult jniResult = {.value = value, .exception = exception};
return jniResult;
}
JniExceptionDetails getExceptionDetails(jthrowable exception) {
JniExceptionDetails details;
details.message = (*jniEnv)->CallObjectMethod(
jniEnv, exception, exceptionMethods.toStringMethod);
jobject buffer =
(*jniEnv)->NewObject(jniEnv, exceptionMethods.byteArrayOutputStreamClass,
exceptionMethods.byteArrayOutputStreamCtor);
jobject printStream =
(*jniEnv)->NewObject(jniEnv, exceptionMethods.printStreamClass,
exceptionMethods.printStreamCtor, buffer);
(*jniEnv)->CallVoidMethod(
jniEnv, exception, exceptionMethods.printStackTraceMethod, printStream);
details.stacktrace = (*jniEnv)->CallObjectMethod(
jniEnv, buffer, exceptionMethods.toStringMethod);
details.message = to_global_ref(details.message);
details.stacktrace = to_global_ref(details.stacktrace);
return details;
}
JniAccessorsStruct accessors = {
.getClass = getClass,
.getFieldID = getFieldID,
.getStaticFieldID = getStaticFieldID,
.getMethodID = getMethodID,
.getStaticMethodID = getStaticMethodID,
.newObject = newObject,
.newPrimitiveArray = newPrimitiveArray,
.newObjectArray = newObjectArray,
.getArrayElement = getArrayElement,
.callMethod = callMethod,
.callStaticMethod = callStaticMethod,
.getField = getField,
.getStaticField = getStaticField,
.getExceptionDetails = getExceptionDetails,
};
FFI_PLUGIN_EXPORT JniAccessorsStruct* GetAccessors() {
return &accessors;
}
// These will not be required after migrating to Dart-only bindings.
FFI_PLUGIN_EXPORT JniContext* GetJniContextPtr() {
return jni;
}
FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv() {
if (jni_context.jvm == NULL) {
return NULL;
}
attach_thread();
return jniEnv;
}
FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data) {
return Dart_InitializeApiDL(data);
}
// com.github.dart_lang.jni.DartException
jclass _c_DartException = NULL;
jmethodID _m_DartException__ctor = NULL;
FFI_PLUGIN_EXPORT JniResult DartException__ctor(jstring message) {
attach_thread();
load_class_global_ref(&_c_DartException,
"com/github/dart_lang/jni/PortProxy$DartException");
if (_c_DartException == NULL)
return (JniResult){.value = {.j = 0}, .exception = check_exception()};
load_method(_c_DartException, &_m_DartException__ctor, "<init>",
"(Ljava/lang/String;)V");
if (_m_DartException__ctor == NULL)
return (JniResult){.value = {.j = 0}, .exception = check_exception()};
jobject _result = (*jniEnv)->NewObject(jniEnv, _c_DartException,
_m_DartException__ctor, message);
jthrowable exception = check_exception();
if (exception == NULL) {
_result = to_global_ref(_result);
}
return (JniResult){.value = {.l = _result}, .exception = check_exception()};
}
JNIEXPORT void JNICALL
Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env,
jclass clazz,
jlong port,
jobject result) {
attach_thread();
Dart_CObject c_post;
c_post.type = Dart_CObject_kInt64;
c_post.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, result));
Dart_PostCObject_DL(port, &c_post);
}
// com.github.dart_lang.jni.PortContinuation
jclass _c_PortContinuation = NULL;
jmethodID _m_PortContinuation__ctor = NULL;
FFI_PLUGIN_EXPORT
JniResult PortContinuation__ctor(int64_t j) {
attach_thread();
load_class_global_ref(&_c_PortContinuation,
"com/github/dart_lang/jni/PortContinuation");
if (_c_PortContinuation == NULL)
return (JniResult){.value = {.j = 0}, .exception = check_exception()};
load_method(_c_PortContinuation, &_m_PortContinuation__ctor, "<init>",
"(J)V");
if (_m_PortContinuation__ctor == NULL)
return (JniResult){.value = {.j = 0}, .exception = check_exception()};
jobject _result = (*jniEnv)->NewObject(jniEnv, _c_PortContinuation,
_m_PortContinuation__ctor, j);
jthrowable exception = check_exception();
if (exception == NULL) {
_result = to_global_ref(_result);
}
return (JniResult){.value = {.l = _result}, .exception = check_exception()};
}
// com.github.dart_lang.jni.PortProxy
jclass _c_PortProxy = NULL;
jmethodID _m_PortProxy__newInstance = NULL;
FFI_PLUGIN_EXPORT
JniResult PortProxy__newInstance(jobject binaryName,
int64_t port,
int64_t functionPtr) {
attach_thread();
load_class_global_ref(&_c_PortProxy, "com/github/dart_lang/jni/PortProxy");
if (_c_PortProxy == NULL)
return (JniResult){.value = {.j = 0}, .exception = check_exception()};
load_static_method(_c_PortProxy, &_m_PortProxy__newInstance, "newInstance",
"(Ljava/lang/String;JJJ)Ljava/lang/Object;");
if (_m_PortProxy__newInstance == NULL)
return (JniResult){.value = {.j = 0}, .exception = check_exception()};
jobject _result = (*jniEnv)->CallStaticObjectMethod(
jniEnv, _c_PortProxy, _m_PortProxy__newInstance, binaryName, port,
(jlong)Dart_CurrentIsolate_DL(), functionPtr);
return to_global_ref_result(_result);
}
FFI_PLUGIN_EXPORT
void resultFor(CallbackResult* result, jobject object) {
acquire_lock(&result->lock);
result->ready = 1;
result->object = object;
signal_cond(&result->cond);
release_lock(&result->lock);
}
jclass _c_Object = NULL;
jclass _c_Long = NULL;
jmethodID _m_Long_init = NULL;
JNIEXPORT jobjectArray JNICALL
Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env,
jclass clazz,
jlong port,
jlong isolateId,
jlong functionPtr,
jobject proxy,
jstring methodDescriptor,
jobjectArray args) {
CallbackResult* result = (CallbackResult*)malloc(sizeof(CallbackResult));
if (isolateId != (jlong)Dart_CurrentIsolate_DL()) {
init_lock(&result->lock);
init_cond(&result->cond);
acquire_lock(&result->lock);
result->ready = 0;
result->object = NULL;
Dart_CObject c_result;
c_result.type = Dart_CObject_kInt64;
c_result.value.as_int64 = (jlong)result;
Dart_CObject c_method;
c_method.type = Dart_CObject_kInt64;
c_method.value.as_int64 =
(jlong)((*env)->NewGlobalRef(env, methodDescriptor));
Dart_CObject c_args;
c_args.type = Dart_CObject_kInt64;
c_args.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, args));
Dart_CObject* c_post_arr[] = {&c_result, &c_method, &c_args};
Dart_CObject c_post;
c_post.type = Dart_CObject_kArray;
c_post.value.as_array.values = c_post_arr;
c_post.value.as_array.length = sizeof(c_post_arr) / sizeof(c_post_arr[0]);
Dart_PostCObject_DL(port, &c_post);
while (!result->ready) {
wait_for(&result->cond, &result->lock);
}
release_lock(&result->lock);
destroy_lock(&result->lock);
destroy_cond(&result->cond);
} else {
result->object = ((jobject(*)(uint64_t, jobject, jobject))functionPtr)(
port, (*env)->NewGlobalRef(env, methodDescriptor),
(*env)->NewGlobalRef(env, args));
}
// Returning an array of length 2.
// [0]: The result pointer, used for cleaning up the global reference, and
// freeing the memory since we passed the ownership to Java.
// [1]: The returned object.
attach_thread();
load_class_global_ref(&_c_Object, "java/lang/Object");
load_class_global_ref(&_c_Long, "java/lang/Long");
load_method(_c_Long, &_m_Long_init, "<init>", "(J)V");
jobject first = (*env)->NewObject(env, _c_Long, _m_Long_init, (jlong)result);
jobject second = result->object;
jobjectArray arr = (*env)->NewObjectArray(env, 2, _c_Object, NULL);
(*env)->SetObjectArrayElement(env, arr, 0, first);
(*env)->SetObjectArrayElement(env, arr, 1, second);
return arr;
}
JNIEXPORT void JNICALL
Java_com_github_dart_1lang_jni_PortProxy__1cleanUp(JNIEnv* env,
jclass clazz,
jlong resultPtr) {
CallbackResult* result = (CallbackResult*)resultPtr;
(*env)->DeleteGlobalRef(env, result->object);
free(result);
}
JNIEXPORT void JNICALL
Java_com_github_dart_1lang_jni_PortCleaner_clean(JNIEnv* env,
jclass clazz,
jlong port) {
Dart_CObject close_signal;
close_signal.type = Dart_CObject_kNull;
Dart_PostCObject_DL(port, &close_signal);
}