Files
flutter_qjs/android/src/main/jni/native-lib.cpp
2020-08-16 20:05:53 +08:00

183 lines
5.9 KiB
C++

/*
* @Description:
* @Author: ekibun
* @Date: 2020-08-09 18:16:11
* @LastEditors: ekibun
* @LastEditTime: 2020-08-16 19:00:06
*/
#include "java_js_wrapper.hpp"
JNIEnv *getEnv(JavaVM *gJvm)
{
JNIEnv *env;
int status = gJvm->GetEnv((void **)&env, JNI_VERSION_1_6);
if (status < 0)
{
status = gJvm->AttachCurrentThread(&env, NULL);
if (status < 0)
{
return nullptr;
}
}
return env;
}
void jniResultResolve(JNIEnv *env, jobject result, jobject data)
{
jclass jclazz = env->GetObjectClass(result);
jmethodID jmethod = env->GetMethodID(jclazz, "success", "(Ljava/lang/Object;)V");
env->CallVoidMethod(result, jmethod, data);
env->DeleteLocalRef(data);
env->DeleteGlobalRef(result);
}
void jniResultReject(JNIEnv *env, jobject result, std::string reason)
{
jclass jclazz = env->GetObjectClass(result);
jmethodID jmethod = env->GetMethodID(jclazz, "error", "(Ljava/lang/String;)V");
jstring jreason = env->NewStringUTF(reason.c_str());
env->CallVoidMethod(result, jmethod, jreason);
env->DeleteLocalRef(jreason);
env->DeleteGlobalRef(result);
}
void jniChannelInvoke(JNIEnv *env, jobject channel, std::promise<qjs::JSFutureReturn> *promise, std::string method, qjs::Value args, qjs::Engine *engine)
{
jclass jclazz = env->GetObjectClass(channel);
jmethodID jmethod = env->GetMethodID(jclazz, "invokeMethod", "(Ljava/lang/String;Ljava/lang/Object;J)V");
jstring jstrmethod = env->NewStringUTF(method.c_str());
std::map<jobject, jobject> retMap;
retMap[env->NewStringUTF("engine")] = jniWrapPrimity<jlong>(env, (int64_t) engine);
retMap[env->NewStringUTF("args")] = qjs::jsToJava(env, args);
jobject jsargs = jniWrapMap(env, retMap);
env->CallVoidMethod(channel, jmethod, jstrmethod, jsargs, (jlong)promise);
env->DeleteLocalRef(jstrmethod);
env->DeleteLocalRef(jsargs);
}
extern "C" JNIEXPORT jlong JNICALL
Java_soko_ekibun_flutter_1qjs_JniBridge_createEngine(
JNIEnv *env,
jobject thiz,
jobject channel)
{
JavaVM *jvm = nullptr;
env->GetJavaVM(&jvm);
jobject gchannel = env->NewGlobalRef(channel);
qjs::Engine *engine = new qjs::Engine([jvm, gchannel](std::string name, qjs::Value args, qjs::Engine *engine) {
auto promise = new std::promise<qjs::JSFutureReturn>();
JNIEnv *env = getEnv(jvm);
jniChannelInvoke(env, gchannel, promise, name, args, engine);
jvm->DetachCurrentThread();
return promise;
});
return (jlong)engine;
}
extern "C" JNIEXPORT void JNICALL
Java_soko_ekibun_flutter_1qjs_JniBridge_evaluate(
JNIEnv *env,
jobject thiz,
jlong engine,
jstring script,
jstring name,
jobject result)
{
JavaVM *jvm = nullptr;
env->GetJavaVM(&jvm);
jobject gresult = env->NewGlobalRef(result);
((qjs::Engine *)engine)->commit(qjs::EngineTask{
[script = std::string(env->GetStringUTFChars(script, 0)),
name = std::string(env->GetStringUTFChars(name, 0))](qjs::Context &ctx) {
return ctx.eval(script, name.c_str(), JS_EVAL_TYPE_GLOBAL);
},
[jvm, gresult](qjs::Value resolve) {
JNIEnv *env = getEnv(jvm);
jniResultResolve(env, gresult, qjs::jsToJava(env, resolve));
jvm->DetachCurrentThread();
},
[jvm, gresult](qjs::Value reject) {
JNIEnv *env = getEnv(jvm);
jniResultReject(env, gresult, qjs::getStackTrack(reject));
jvm->DetachCurrentThread();
}});
}
extern "C" JNIEXPORT void JNICALL
Java_soko_ekibun_flutter_1qjs_JniBridge_close(
JNIEnv *env,
jobject thiz,
jlong engine)
{
delete (qjs::Engine *)engine;
}
extern "C" JNIEXPORT void JNICALL
Java_soko_ekibun_flutter_1qjs_JniBridge_call(
JNIEnv *env,
jobject thiz,
jlong engine,
jlong function,
jobject args,
jobject result)
{
JavaVM *jvm = nullptr;
env->GetJavaVM(&jvm);
jobject gresult = env->NewGlobalRef(result);
jobject gargs = env->NewGlobalRef(args);
((qjs::Engine *)engine)->commit(qjs::EngineTask{
[jvm, function = (qjs::JSValue *)function, gargs](qjs::Context &ctx) {
JNIEnv *env = getEnv(jvm);
jobjectArray array = jniToArray(env, gargs);
jsize argscount = env->GetArrayLength(array);
qjs::JSValue *callargs = new qjs::JSValue[argscount];
for (jsize i = 0; i < argscount; i++)
{
callargs[i] = qjs::javaToJs(ctx.ctx, env, env->GetObjectArrayElement(array, i));
}
jvm->DetachCurrentThread();
qjs::JSValue ret = JS_Call(ctx.ctx, *function, ctx.global(), (int)argscount, callargs);
qjs::JS_FreeValue(ctx.ctx, *function);
if (qjs::JS_IsException(ret))
throw qjs::exception{};
return qjs::Value{ctx.ctx, ret};
},
[jvm, gresult](qjs::Value resolve) {
JNIEnv *env = getEnv(jvm);
jniResultResolve(env, gresult, qjs::jsToJava(env, resolve));
jvm->DetachCurrentThread();
},
[jvm, gresult](qjs::Value reject) {
JNIEnv *env = getEnv(jvm);
jniResultReject(env, gresult, qjs::getStackTrack(reject));
jvm->DetachCurrentThread();
}});
}
extern "C" JNIEXPORT void JNICALL
Java_soko_ekibun_flutter_1qjs_JniBridge_resolve(
JNIEnv *env,
jobject clazz,
jlong promise, jobject data)
{
JavaVM *jvm = nullptr;
env->GetJavaVM(&jvm);
jobject gdata = env->NewGlobalRef(data);
((std::promise<qjs::JSFutureReturn> *)promise)->set_value((qjs::JSFutureReturn)[jvm, gdata](qjs::JSContext * ctx) {
JNIEnv *env = getEnv(jvm);
qjs::JSValue *ret = new qjs::JSValue{qjs::javaToJs(ctx, env, gdata)};
return qjs::JSOSFutureArgv{1, ret};
});
}
extern "C" JNIEXPORT void JNICALL
Java_soko_ekibun_flutter_1qjs_JniBridge_reject(
JNIEnv *env,
jobject clazz,
jlong promise, jstring data)
{
((std::promise<qjs::JSFutureReturn> *)promise)->set_value((qjs::JSFutureReturn)[data = std::string(env->GetStringUTFChars(data, 0))](qjs::JSContext * ctx) {
qjs::JSValue *ret = new qjs::JSValue{JS_NewString(ctx, data.c_str())};
return qjs::JSOSFutureArgv{-1, ret};
});
}