From 98aec63f53e494c40e1389f2302f5f46d43d3bc1 Mon Sep 17 00:00:00 2001 From: ekibun Date: Thu, 13 Aug 2020 00:03:04 +0800 Subject: [PATCH] code clean up --- .vscode/c_cpp_properties.json | 22 - android/src/main/jni/CMakeLists.txt | 11 +- android/src/main/jni/native-lib.cpp | 30 +- .../src/main/jni => cxx}/js_dart_promise.hpp | 4 +- {android/src/main/jni => cxx}/js_engine.hpp | 4 +- {android/src/main/jni => cxx}/quickjs/VERSION | 0 .../src/main/jni => cxx}/quickjs/cutils.c | 0 .../src/main/jni => cxx}/quickjs/cutils.h | 0 .../jni => cxx}/quickjs/libregexp-opcode.h | 0 .../src/main/jni => cxx}/quickjs/libregexp.c | 0 .../src/main/jni => cxx}/quickjs/libregexp.h | 0 .../jni => cxx}/quickjs/libunicode-table.h | 0 .../src/main/jni => cxx}/quickjs/libunicode.c | 0 .../src/main/jni => cxx}/quickjs/libunicode.h | 0 {android/src/main/jni => cxx}/quickjs/list.h | 0 .../main/jni => cxx}/quickjs/quickjs-atom.h | 0 .../main/jni => cxx}/quickjs/quickjs-opcode.h | 0 .../src/main/jni => cxx}/quickjs/quickjs.c | 0 .../src/main/jni => cxx}/quickjs/quickjs.h | 4 + .../main/jni/quickjs => cxx}/quickjspp.hpp | 2 +- windows/flutter_qjs_plugin.cpp | 39 +- windows/js_dart_promise.hpp | 207 --- windows/js_engine.hpp | 192 --- windows/quickjs/quickjs/list.h | 100 -- windows/quickjs/quickjs/quickjs-libc.h | 58 - windows/quickjs/quickjs/quickjs.h | 1031 ------------- windows/quickjs/quickjspp.hpp | 1304 ----------------- 27 files changed, 51 insertions(+), 2957 deletions(-) delete mode 100644 .vscode/c_cpp_properties.json rename {android/src/main/jni => cxx}/js_dart_promise.hpp (98%) rename {android/src/main/jni => cxx}/js_engine.hpp (98%) rename {android/src/main/jni => cxx}/quickjs/VERSION (100%) rename {android/src/main/jni => cxx}/quickjs/cutils.c (100%) rename {android/src/main/jni => cxx}/quickjs/cutils.h (100%) rename {android/src/main/jni => cxx}/quickjs/libregexp-opcode.h (100%) rename {android/src/main/jni => cxx}/quickjs/libregexp.c (100%) rename {android/src/main/jni => cxx}/quickjs/libregexp.h (100%) rename {android/src/main/jni => cxx}/quickjs/libunicode-table.h (100%) rename {android/src/main/jni => cxx}/quickjs/libunicode.c (100%) rename {android/src/main/jni => cxx}/quickjs/libunicode.h (100%) rename {android/src/main/jni => cxx}/quickjs/list.h (100%) rename {android/src/main/jni => cxx}/quickjs/quickjs-atom.h (100%) rename {android/src/main/jni => cxx}/quickjs/quickjs-opcode.h (100%) rename {android/src/main/jni => cxx}/quickjs/quickjs.c (100%) rename {android/src/main/jni => cxx}/quickjs/quickjs.h (99%) rename {android/src/main/jni/quickjs => cxx}/quickjspp.hpp (99%) delete mode 100644 windows/js_dart_promise.hpp delete mode 100644 windows/js_engine.hpp delete mode 100644 windows/quickjs/quickjs/list.h delete mode 100644 windows/quickjs/quickjs/quickjs-libc.h delete mode 100644 windows/quickjs/quickjs/quickjs.h delete mode 100644 windows/quickjs/quickjspp.hpp diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 1cc6949..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**", - "C:\\Users\\ekibun\\AppData\\Local\\Android\\Sdk\\ndk\\21.3.6528147\\toolchains\\llvm\\prebuilt\\windows-x86_64\\sysroot\\usr\\include" - ], - "defines": [ - "_DEBUG", - "UNICODE", - "_UNICODE" - ], - "windowsSdkVersion": "10.0.18362.0", - "compilerPath": "C:\\Users\\ekibun\\AppData\\Local\\Android\\Sdk\\cmake\\3.10.2.4988404\\bin\\cmake.exe", - "cStandard": "c11", - "cppStandard": "c++17", - "intelliSenseMode": "gcc-x64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/android/src/main/jni/CMakeLists.txt b/android/src/main/jni/CMakeLists.txt index de5c3fd..d5994bc 100644 --- a/android/src/main/jni/CMakeLists.txt +++ b/android/src/main/jni/CMakeLists.txt @@ -5,6 +5,7 @@ cmake_minimum_required(VERSION 3.4.1) set(JNI_LIB_NAME libjsengine) +set(QUICK_JS_LIB_DIR ../../../../cxx) # Creates and names a library, sets it as either STATIC @@ -22,13 +23,13 @@ add_library( # Sets the name of the library. native-lib.cpp ) # quickjs -file (STRINGS "quickjs/VERSION" QUICKJS_VERSION) +file (STRINGS "${QUICK_JS_LIB_DIR}/quickjs/VERSION" QUICKJS_VERSION) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCONFIG_VERSION=\\\"${QUICKJS_VERSION}\\\"") target_sources(${JNI_LIB_NAME} PUBLIC - quickjs/cutils.c - quickjs/libregexp.c - quickjs/libunicode.c - quickjs/quickjs.c + ${QUICK_JS_LIB_DIR}/quickjs/cutils.c + ${QUICK_JS_LIB_DIR}/quickjs/libregexp.c + ${QUICK_JS_LIB_DIR}/quickjs/libunicode.c + ${QUICK_JS_LIB_DIR}/quickjs/quickjs.c ) # Searches for a specified prebuilt library and stores the path as a diff --git a/android/src/main/jni/native-lib.cpp b/android/src/main/jni/native-lib.cpp index 1640387..b02b903 100644 --- a/android/src/main/jni/native-lib.cpp +++ b/android/src/main/jni/native-lib.cpp @@ -3,17 +3,14 @@ * @Author: ekibun * @Date: 2020-08-09 18:16:11 * @LastEditors: ekibun - * @LastEditTime: 2020-08-12 23:11:35 + * @LastEditTime: 2020-08-12 23:37:28 */ #include #include -#include "js_engine.hpp" +#include "../../../../cxx/js_engine.hpp" qjs::Engine *engine = nullptr; -// static jobject gClassLoader; -// static jmethodID gFindClassMethod; - JNIEnv *getEnv(JavaVM *gJvm) { JNIEnv *env; @@ -29,26 +26,6 @@ JNIEnv *getEnv(JavaVM *gJvm) return env; } -// JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) -// { -// JNIEnv *env = getEnv(pjvm); -// auto randomClass = env->FindClass("soko/ekibun/flutter_qjs/ResultWrapper"); -// jclass classClass = env->GetObjectClass(randomClass); -// auto classLoaderClass = env->FindClass("java/lang/ClassLoader"); -// auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", -// "()Ljava/lang/ClassLoader;"); -// gClassLoader = env->NewGlobalRef(env->CallObjectMethod(randomClass, getClassLoaderMethod)); -// gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass", -// "(Ljava/lang/String;)Ljava/lang/Class;"); - -// return JNI_VERSION_1_6; -// } - -// jclass findClass(JNIEnv *env, const char *name) -// { -// return static_cast(env->CallObjectMethod(gClassLoader, gFindClassMethod, env->NewStringUTF(name))); -// } - void jniResultResolve(JavaVM *jvm, jobject result, std::string data) { JNIEnv *env = getEnv(jvm); @@ -122,12 +99,9 @@ Java_soko_ekibun_flutter_1qjs_JniBridge_evaluate( env->GetStringUTFChars(name, 0), [jvm, gresult](std::string resolve) { jniResultResolve(jvm, gresult, resolve); - // flutter::EncodableValue response(resolve); - // presult->Success(&response); }, [jvm, gresult](std::string reject) { jniResultReject(jvm, gresult, reject); - // presult->Error("FlutterJSException", reject); }}); } diff --git a/android/src/main/jni/js_dart_promise.hpp b/cxx/js_dart_promise.hpp similarity index 98% rename from android/src/main/jni/js_dart_promise.hpp rename to cxx/js_dart_promise.hpp index 27e5e82..fc6843b 100644 --- a/android/src/main/jni/js_dart_promise.hpp +++ b/cxx/js_dart_promise.hpp @@ -3,10 +3,10 @@ * @Author: ekibun * @Date: 2020-08-07 13:55:52 * @LastEditors: ekibun - * @LastEditTime: 2020-08-12 14:24:42 + * @LastEditTime: 2020-08-12 23:30:57 */ #pragma once -#include "quickjs/quickjspp.hpp" +#include "quickjspp.hpp" #include "quickjs/list.h" #include #include diff --git a/android/src/main/jni/js_engine.hpp b/cxx/js_engine.hpp similarity index 98% rename from android/src/main/jni/js_engine.hpp rename to cxx/js_engine.hpp index 188fd77..73c963e 100644 --- a/android/src/main/jni/js_engine.hpp +++ b/cxx/js_engine.hpp @@ -3,7 +3,7 @@ * @Author: ekibun * @Date: 2020-08-08 10:30:59 * @LastEditors: ekibun - * @LastEditTime: 2020-08-12 13:53:59 + * @LastEditTime: 2020-08-12 23:27:20 */ #pragma once @@ -15,7 +15,7 @@ #include #include "js_dart_promise.hpp" -#include "quickjs/quickjspp.hpp" +#include "quickjspp.hpp" namespace qjs { diff --git a/android/src/main/jni/quickjs/VERSION b/cxx/quickjs/VERSION similarity index 100% rename from android/src/main/jni/quickjs/VERSION rename to cxx/quickjs/VERSION diff --git a/android/src/main/jni/quickjs/cutils.c b/cxx/quickjs/cutils.c similarity index 100% rename from android/src/main/jni/quickjs/cutils.c rename to cxx/quickjs/cutils.c diff --git a/android/src/main/jni/quickjs/cutils.h b/cxx/quickjs/cutils.h similarity index 100% rename from android/src/main/jni/quickjs/cutils.h rename to cxx/quickjs/cutils.h diff --git a/android/src/main/jni/quickjs/libregexp-opcode.h b/cxx/quickjs/libregexp-opcode.h similarity index 100% rename from android/src/main/jni/quickjs/libregexp-opcode.h rename to cxx/quickjs/libregexp-opcode.h diff --git a/android/src/main/jni/quickjs/libregexp.c b/cxx/quickjs/libregexp.c similarity index 100% rename from android/src/main/jni/quickjs/libregexp.c rename to cxx/quickjs/libregexp.c diff --git a/android/src/main/jni/quickjs/libregexp.h b/cxx/quickjs/libregexp.h similarity index 100% rename from android/src/main/jni/quickjs/libregexp.h rename to cxx/quickjs/libregexp.h diff --git a/android/src/main/jni/quickjs/libunicode-table.h b/cxx/quickjs/libunicode-table.h similarity index 100% rename from android/src/main/jni/quickjs/libunicode-table.h rename to cxx/quickjs/libunicode-table.h diff --git a/android/src/main/jni/quickjs/libunicode.c b/cxx/quickjs/libunicode.c similarity index 100% rename from android/src/main/jni/quickjs/libunicode.c rename to cxx/quickjs/libunicode.c diff --git a/android/src/main/jni/quickjs/libunicode.h b/cxx/quickjs/libunicode.h similarity index 100% rename from android/src/main/jni/quickjs/libunicode.h rename to cxx/quickjs/libunicode.h diff --git a/android/src/main/jni/quickjs/list.h b/cxx/quickjs/list.h similarity index 100% rename from android/src/main/jni/quickjs/list.h rename to cxx/quickjs/list.h diff --git a/android/src/main/jni/quickjs/quickjs-atom.h b/cxx/quickjs/quickjs-atom.h similarity index 100% rename from android/src/main/jni/quickjs/quickjs-atom.h rename to cxx/quickjs/quickjs-atom.h diff --git a/android/src/main/jni/quickjs/quickjs-opcode.h b/cxx/quickjs/quickjs-opcode.h similarity index 100% rename from android/src/main/jni/quickjs/quickjs-opcode.h rename to cxx/quickjs/quickjs-opcode.h diff --git a/android/src/main/jni/quickjs/quickjs.c b/cxx/quickjs/quickjs.c similarity index 100% rename from android/src/main/jni/quickjs/quickjs.c rename to cxx/quickjs/quickjs.c diff --git a/android/src/main/jni/quickjs/quickjs.h b/cxx/quickjs/quickjs.h similarity index 99% rename from android/src/main/jni/quickjs/quickjs.h rename to cxx/quickjs/quickjs.h index db29372..6a38b34 100644 --- a/android/src/main/jni/quickjs/quickjs.h +++ b/cxx/quickjs/quickjs.h @@ -215,7 +215,11 @@ typedef struct JSValue { #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) #define JS_VALUE_GET_PTR(v) ((v).u.ptr) +#ifdef _MSC_VER +#define JS_MKVAL(tag, val) JSValue { JSValueUnion { val }, tag } +#else #define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } +#endif #define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } #define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) diff --git a/android/src/main/jni/quickjs/quickjspp.hpp b/cxx/quickjspp.hpp similarity index 99% rename from android/src/main/jni/quickjs/quickjspp.hpp rename to cxx/quickjspp.hpp index b02af4b..6aa63f9 100644 --- a/android/src/main/jni/quickjs/quickjspp.hpp +++ b/cxx/quickjspp.hpp @@ -12,7 +12,7 @@ #include namespace qjs { -#include "quickjs.h" +#include "quickjs/quickjs.h" /** Exception type. diff --git a/windows/flutter_qjs_plugin.cpp b/windows/flutter_qjs_plugin.cpp index 37b5f22..6f43bbc 100644 --- a/windows/flutter_qjs_plugin.cpp +++ b/windows/flutter_qjs_plugin.cpp @@ -3,14 +3,12 @@ // This must be included before many other Windows headers. #include -// For getPlatformVersion; remove unless needed for your plugin implementation. -#include - #include #include #include +#include -#include "js_engine.hpp" +#include "../cxx/js_engine.hpp" namespace { @@ -32,6 +30,37 @@ namespace }; std::shared_ptr> channel; + std::future invokeChannelMethod(std::string name, std::string args) + { + auto promise = new std::promise(); + channel->InvokeMethod( + name, + std::make_unique(args), + std::make_unique>( + (flutter::ResultHandlerSuccess)[promise]( + const flutter::EncodableValue *result) { + promise->set_value((qjs::JSFutureReturn)[rep = std::get(*result)](qjs::JSContext * ctx) { + qjs::JSValue *ret = new qjs::JSValue{JS_NewString(ctx, rep.c_str())}; + return qjs::JSOSFutureArgv{1, ret}; + }); + }, + (flutter::ResultHandlerError)[promise]( + const std::string &error_code, + const std::string &error_message, + const flutter::EncodableValue *error_details) { + promise->set_value((qjs::JSFutureReturn)[error_message](qjs::JSContext * ctx) { + qjs::JSValue *ret = new qjs::JSValue{JS_NewString(ctx, error_message.c_str())}; + return qjs::JSOSFutureArgv{-1, ret}; + }); + }, + (flutter::ResultHandlerNotImplemented)[promise]() { + promise->set_value((qjs::JSFutureReturn)[](qjs::JSContext * ctx) { + qjs::JSValue *ret = new qjs::JSValue{JS_NewString(ctx, "NotImplemented")}; + return qjs::JSOSFutureArgv{-1, ret}; + }); + })); + return promise->get_future(); + } // static void FlutterQjsPlugin::RegisterWithRegistrar( @@ -81,7 +110,7 @@ namespace // for the relevant Flutter APIs. if (method_call.method_name().compare("initEngine") == 0) { - engine = new qjs::Engine(channel); + engine = new qjs::Engine((qjs::DartChannel)invokeChannelMethod); flutter::EncodableValue response((long)engine); result->Success(&response); } diff --git a/windows/js_dart_promise.hpp b/windows/js_dart_promise.hpp deleted file mode 100644 index ff2e899..0000000 --- a/windows/js_dart_promise.hpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * @Description: - * @Author: ekibun - * @Date: 2020-08-07 13:55:52 - * @LastEditors: ekibun - * @LastEditTime: 2020-08-08 16:54:23 - */ -#pragma once -#include "quickjs/quickjspp.hpp" -#include "quickjs/quickjs/list.h" -#include -#include - -namespace qjs -{ - static JSClassID js_dart_promise_class_id; - - typedef struct - { - int count; - JSValue *argv; - } JSOSFutureArgv; - - using JSFutureReturn = std::function; - - typedef struct - { - struct list_head link; - std::shared_future future; - JSValue resolve; - JSValue reject; - } JSOSFuture; - - typedef struct JSThreadState - { - struct list_head os_future; /* list of JSOSFuture.link */ - std::shared_ptr> channel; - } JSThreadState; - - static JSValue js_add_future(Value resolve, Value reject, std::shared_future future) - { - JSRuntime *rt = JS_GetRuntime(resolve.ctx); - JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt); - JSValueConst jsResolve, jsReject; - JSOSFuture *th; - JSValue obj; - - jsResolve = resolve.v; - if (!JS_IsFunction(resolve.ctx, jsResolve)) - return JS_ThrowTypeError(resolve.ctx, "resolve not a function"); - jsReject = reject.v; - if (!JS_IsFunction(reject.ctx, jsReject)) - return JS_ThrowTypeError(reject.ctx, "reject not a function"); - obj = JS_NewObjectClass(resolve.ctx, js_dart_promise_class_id); - if (JS_IsException(obj)) - return obj; - th = (JSOSFuture *)js_mallocz(resolve.ctx, sizeof(*th)); - if (!th) - { - JS_FreeValue(resolve.ctx, obj); - return JS_EXCEPTION; - } - th->future = future; - th->resolve = JS_DupValue(resolve.ctx, jsResolve); - th->reject = JS_DupValue(reject.ctx, jsReject); - list_add_tail(&th->link, &ts->os_future); - JS_SetOpaque(obj, th); - return obj; - } - - JSValue js_dart_future(Value resolve, Value reject, std::string name, std::string args) - { - auto promise = new std::promise(); - JSRuntime *rt = JS_GetRuntime(resolve.ctx); - JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt); - ts->channel->InvokeMethod( - name, - std::make_unique(args), - std::make_unique>( - (flutter::ResultHandlerSuccess)[promise]( - const flutter::EncodableValue *result) { - promise->set_value((JSFutureReturn)[rep = std::get(*result)](JSContext * ctx) { - JSValue *ret = new JSValue{JS_NewString(ctx, rep.c_str())}; - return JSOSFutureArgv{1, ret}; - }); - }, - (flutter::ResultHandlerError)[promise]( - const std::string &error_code, - const std::string &error_message, - const flutter::EncodableValue *error_details) { - promise->set_value((JSFutureReturn)[error_message](JSContext * ctx) { - JSValue *ret = new JSValue{JS_NewString(ctx, error_message.c_str())}; - return JSOSFutureArgv{-1, ret}; - }); - }, - (flutter::ResultHandlerNotImplemented)[promise]() { - promise->set_value((JSFutureReturn)[](JSContext * ctx) { - JSValue *ret = new JSValue{JS_NewString(ctx, "NotImplemented")}; - return JSOSFutureArgv{-1, ret}; - }); - })); - return js_add_future(resolve, reject, promise->get_future()); - } - - static void unlink_future(JSRuntime *rt, JSOSFuture *th) - { - if (th->link.prev) - { - list_del(&th->link); - th->link.prev = th->link.next = NULL; - } - } - - static void free_future(JSRuntime *rt, JSOSFuture *th) - { - JS_FreeValueRT(rt, th->resolve); - JS_FreeValueRT(rt, th->reject); - js_free_rt(rt, th); - } - - void js_init_handlers(JSRuntime *rt, std::shared_ptr> channel) - { - JSThreadState *ts = (JSThreadState *)malloc(sizeof(*ts)); - if (!ts) - { - fprintf(stderr, "Could not allocate memory for the worker"); - exit(1); - } - memset(ts, 0, sizeof(*ts)); - init_list_head(&ts->os_future); - ts->channel = channel; - - JS_SetRuntimeOpaque(rt, ts); - } - - void js_free_handlers(JSRuntime *rt) - { - JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt); - struct list_head *el, *el1; - - list_for_each_safe(el, el1, &ts->os_future) - { - JSOSFuture *th = list_entry(el, JSOSFuture, link); - th->future.get(); - unlink_future(rt, th); - free_future(rt, th); - } - ts->channel = nullptr; - free(ts); - JS_SetRuntimeOpaque(rt, NULL); /* fail safe */ - } - - static void call_handler(JSContext *ctx, JSValueConst func, int count, JSValue *argv) - { - JSValue ret, func1; - /* 'func' might be destroyed when calling itself (if it frees the - handler), so must take extra care */ - func1 = JS_DupValue(ctx, func); - ret = JS_Call(ctx, func1, JS_UNDEFINED, count, argv); - JS_FreeValue(ctx, func1); - if (JS_IsException(ret)) - throw exception{}; - JS_FreeValue(ctx, ret); - } - - static int js_dart_poll(JSContext *ctx) - { - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt); - struct list_head *el; - - /* XXX: handle signals if useful */ - - if (list_empty(&ts->os_future)) - return -1; /* no more events */ - - /* XXX: only timers and basic console input are supported */ - if (!list_empty(&ts->os_future)) - { - list_for_each(el, &ts->os_future) - { - JSOSFuture *th = list_entry(el, JSOSFuture, link); - if (th->future._Is_ready()) - { - JSOSFutureArgv argv = th->future.get()(ctx); - JSValue resolve, reject; - int64_t delay; - /* the timer expired */ - resolve = th->resolve; - th->resolve = JS_UNDEFINED; - reject = th->reject; - th->reject = JS_UNDEFINED; - unlink_future(rt, th); - free_future(rt, th); - call_handler(ctx, argv.count < 0 ? reject : resolve, abs(argv.count), argv.argv); - for (int i = 0; i < abs(argv.count); ++i) - JS_FreeValue(ctx, argv.argv[i]); - JS_FreeValue(ctx, resolve); - JS_FreeValue(ctx, reject); - delete argv.argv; - return 0; - } - } - } - return 0; - } -} // namespace qjs \ No newline at end of file diff --git a/windows/js_engine.hpp b/windows/js_engine.hpp deleted file mode 100644 index dfe8e30..0000000 --- a/windows/js_engine.hpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * @Description: - * @Author: ekibun - * @Date: 2020-08-08 10:30:59 - * @LastEditors: ekibun - * @LastEditTime: 2020-08-08 17:05:22 - */ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "js_dart_promise.hpp" -#include "quickjs/quickjspp.hpp" - -namespace qjs -{ - struct EngineTask - { - std::string command; - std::string name; - std::function resolve; - std::function reject; - }; - - struct EngineTaskResolver - { - Value result; - std::function resolve; - std::function reject; - }; - - std::string getStackTrack(Value exc) - { - std::string err = (std::string)exc; - if ((bool)exc["stack"]) - err += "\n" + (std::string)exc["stack"]; - return err; - } - - class Engine - { - // 引擎线程 - std::thread thread; - // 任务队列 - std::queue tasks; - // 同步 - std::mutex m_lock; - // 是否关闭提交 - std::atomic stoped; - - void handleException(qjs::Value exc) - { - std::cout << getStackTrack(exc) << std::endl; - } - - public: - inline Engine(std::shared_ptr> channel) : stoped{false} - { - thread = std::thread([this, channel] { // 工作线程函数 - // 创建运行环境 - Runtime rt; - js_init_handlers(rt.rt, channel); - Context ctx(rt); - auto &module = ctx.addModule("__DartImpl"); - module.function<&js_dart_future>("__invoke"); - ctx.eval( - R"xxx( - import * as __DartImpl from "__DartImpl"; - globalThis.dart = (method, ...args) => new Promise((res, rej) => - __DartImpl.__invoke((v) => res(JSON.parse(v)), rej, method, JSON.stringify(args))); - )xxx", - "", JS_EVAL_TYPE_MODULE); - std::vector unresolvedTask; - // 循环 - while (!this->stoped) - { - // 获取待执行的task - EngineTask task; - { // 获取一个待执行的 task - std::unique_lock lock{this->m_lock}; // unique_lock 相比 lock_guard 的好处是:可以随时 unlock() 和 lock() - if (!this->tasks.empty()) - { - task = this->tasks.front(); // 取一个 task - this->tasks.pop(); - } - } - // 执行task - if (task.resolve) - try - { - ctx.global()["__evalstr"] = JS_NewString(ctx.ctx, task.command.c_str()); - Value ret = ctx.eval( - R"xxx( - (() => { - const __ret = Promise.resolve(eval(__evalstr)) - .then(v => { - __ret.__value = v; - __ret.__resolved = true; - }).catch(e => { - __ret.__error = e; - __ret.__rejected = true; - }); - return __ret; - })() - )xxx", - task.name.c_str()); - unresolvedTask.emplace_back(EngineTaskResolver{ret, std::move(task.resolve), std::move(task.reject)}); - } - catch (exception e) - { - task.reject(getStackTrack(ctx.getException())); - } - // 执行microtask - JSContext *pctx; - for (;;) - { - int err = JS_ExecutePendingJob(rt.rt, &pctx); - if (err <= 0) - { - if (err < 0) - std::cout << getStackTrack(ctx.getException()) << std::endl; - break; - } - } - // TODO 检查promise状态 - for (auto it = unresolvedTask.begin(); it != unresolvedTask.end();) - { - bool finished = false; - if (it->result["__resolved"]) - { - it->resolve((std::string)it->result["__value"]); - finished = true; - }; - if (it->result["__rejected"]) - { - it->reject(getStackTrack(it->result["__error"])); - finished = true; - }; - if (finished) - it = unresolvedTask.erase(it); - else - ++it; - } - // 检查dart交互 - bool idle = true; - try - { - idle = js_dart_poll(ctx.ctx); - } - catch (exception e) - { - handleException(ctx.getException()); - } - // 空闲时reject所有task - if (idle && !JS_IsJobPending(rt.rt) && !unresolvedTask.empty()) - { - for (EngineTaskResolver &task : unresolvedTask) - { - task.reject("Promise cannot resolve"); - } - unresolvedTask.clear(); - } - } - js_free_handlers(rt.rt); - }); - } - inline ~Engine() - { - stoped.store(true); - if (thread.joinable()) - thread.join(); // 等待任务结束, 前提:线程一定会执行完 - } - - public: - // 提交一个任务 - void commit(EngineTask task) - { - if (stoped.load()) // stop == true ?? - throw std::runtime_error("commit on stopped engine."); - { // 添加任务到队列 - std::lock_guard lock{m_lock}; //对当前块的语句加锁 lock_guard 是 mutex 的 stack 封装类,构造的时候 lock(),析构的时候 unlock() - tasks.emplace(task); - } - } - }; - -} // namespace qjs diff --git a/windows/quickjs/quickjs/list.h b/windows/quickjs/quickjs/list.h deleted file mode 100644 index 0a1bc5a..0000000 --- a/windows/quickjs/quickjs/list.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Linux klist like system - * - * Copyright (c) 2016-2017 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef LIST_H -#define LIST_H - -#ifndef NULL -#include -#endif - -struct list_head { - struct list_head *prev; - struct list_head *next; -}; - -#define LIST_HEAD_INIT(el) { &(el), &(el) } - -/* return the pointer of type 'type *' containing 'el' as field 'member' */ -#define list_entry(el, type, member) \ - ((type *)((uint8_t *)(el) - offsetof(type, member))) - -static inline void init_list_head(struct list_head *head) -{ - head->prev = head; - head->next = head; -} - -/* insert 'el' between 'prev' and 'next' */ -static inline void __list_add(struct list_head *el, - struct list_head *prev, struct list_head *next) -{ - prev->next = el; - el->prev = prev; - el->next = next; - next->prev = el; -} - -/* add 'el' at the head of the list 'head' (= after element head) */ -static inline void list_add(struct list_head *el, struct list_head *head) -{ - __list_add(el, head, head->next); -} - -/* add 'el' at the end of the list 'head' (= before element head) */ -static inline void list_add_tail(struct list_head *el, struct list_head *head) -{ - __list_add(el, head->prev, head); -} - -static inline void list_del(struct list_head *el) -{ - struct list_head *prev, *next; - prev = el->prev; - next = el->next; - prev->next = next; - next->prev = prev; - el->prev = NULL; /* fail safe */ - el->next = NULL; /* fail safe */ -} - -static inline int list_empty(struct list_head *el) -{ - return el->next == el; -} - -#define list_for_each(el, head) \ - for(el = (head)->next; el != (head); el = el->next) - -#define list_for_each_safe(el, el1, head) \ - for(el = (head)->next, el1 = el->next; el != (head); \ - el = el1, el1 = el->next) - -#define list_for_each_prev(el, head) \ - for(el = (head)->prev; el != (head); el = el->prev) - -#define list_for_each_prev_safe(el, el1, head) \ - for(el = (head)->prev, el1 = el->prev; el != (head); \ - el = el1, el1 = el->prev) - -#endif /* LIST_H */ diff --git a/windows/quickjs/quickjs/quickjs-libc.h b/windows/quickjs/quickjs/quickjs-libc.h deleted file mode 100644 index b105028..0000000 --- a/windows/quickjs/quickjs/quickjs-libc.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * QuickJS C library - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef QUICKJS_LIBC_H -#define QUICKJS_LIBC_H - -#include -#include - -#include "quickjs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); -JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); -void js_std_add_helpers(JSContext *ctx, int argc, char **argv); -void js_std_loop(JSContext *ctx); -void js_std_init_handlers(JSRuntime *rt); -void js_std_free_handlers(JSRuntime *rt); -void js_std_dump_error(JSContext *ctx); -uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); -int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, - JS_BOOL use_realpath, JS_BOOL is_main); -JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque); -void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int flags); -void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, - JSValueConst reason, - JS_BOOL is_handled, void *opaque); - -#ifdef __cplusplus -} /* extern "C" { */ -#endif - -#endif /* QUICKJS_LIBC_H */ diff --git a/windows/quickjs/quickjs/quickjs.h b/windows/quickjs/quickjs/quickjs.h deleted file mode 100644 index cb475b2..0000000 --- a/windows/quickjs/quickjs/quickjs.h +++ /dev/null @@ -1,1031 +0,0 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2020 Fabrice Bellard - * Copyright (c) 2017-2020 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef QUICKJS_H -#define QUICKJS_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define js_likely(x) __builtin_expect(!!(x), 1) -#define js_unlikely(x) __builtin_expect(!!(x), 0) -#define js_force_inline inline __attribute__((always_inline)) -#define __js_printf_like(f, a) __attribute__((format(printf, f, a))) -#else -#define js_likely(x) (x) -#define js_unlikely(x) (x) -#define js_force_inline inline -#define __js_printf_like(a, b) -#endif - -#define JS_BOOL int - -typedef struct JSRuntime JSRuntime; -typedef struct JSContext JSContext; -typedef struct JSObject JSObject; -typedef struct JSClass JSClass; -typedef uint32_t JSClassID; -typedef uint32_t JSAtom; - -#if INTPTR_MAX >= INT64_MAX -#define JS_PTR64 -#define JS_PTR64_DEF(a) a -#else -#define JS_PTR64_DEF(a) -#endif - -#ifndef JS_PTR64 -#define JS_NAN_BOXING -#endif - -enum { - /* all tags with a reference count are negative */ - JS_TAG_FIRST = -11, /* first negative tag */ - JS_TAG_BIG_DECIMAL = -11, - JS_TAG_BIG_INT = -10, - JS_TAG_BIG_FLOAT = -9, - JS_TAG_SYMBOL = -8, - JS_TAG_STRING = -7, - JS_TAG_MODULE = -3, /* used internally */ - JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ - JS_TAG_OBJECT = -1, - - JS_TAG_INT = 0, - JS_TAG_BOOL = 1, - JS_TAG_NULL = 2, - JS_TAG_UNDEFINED = 3, - JS_TAG_UNINITIALIZED = 4, - JS_TAG_CATCH_OFFSET = 5, - JS_TAG_EXCEPTION = 6, - JS_TAG_FLOAT64 = 7, - /* any larger tag is FLOAT64 if JS_NAN_BOXING */ -}; - -typedef struct JSRefCountHeader { - int ref_count; -} JSRefCountHeader; - -#define JS_FLOAT64_NAN NAN - -#ifdef CONFIG_CHECK_JSVALUE -/* JSValue consistency : it is not possible to run the code in this - mode, but it is useful to detect simple reference counting - errors. It would be interesting to modify a static C analyzer to - handle specific annotations (clang has such annotations but only - for objective C) */ -typedef struct __JSValue *JSValue; -typedef const struct __JSValue *JSValueConst; - -#define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v) & 0xf) -/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ -#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) -#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4) -#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v) -#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v) -#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf) - -#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag)) -#define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag)) - -#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) - -#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1) - -static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) -{ - return JS_MKVAL(JS_TAG_FLOAT64, (int)d); -} - -static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) -{ - return 0; -} - -#elif defined(JS_NAN_BOXING) - -typedef uint64_t JSValue; - -#define JSValueConst JSValue - -#define JS_VALUE_GET_TAG(v) (int)((v) >> 32) -#define JS_VALUE_GET_INT(v) (int)(v) -#define JS_VALUE_GET_BOOL(v) (int)(v) -#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v) - -#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) -#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr)) - -#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */ - -static inline double JS_VALUE_GET_FLOAT64(JSValue v) -{ - union { - JSValue v; - double d; - } u; - u.v = v; - u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32; - return u.d; -} - -#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) - -static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) -{ - union { - double d; - uint64_t u64; - } u; - JSValue v; - u.d = d; - /* normalize NaN */ - if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000)) - v = JS_NAN; - else - v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32); - return v; -} - -#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST)) - -/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ -static inline int JS_VALUE_GET_NORM_TAG(JSValue v) -{ - uint32_t tag; - tag = JS_VALUE_GET_TAG(v); - if (JS_TAG_IS_FLOAT64(tag)) - return JS_TAG_FLOAT64; - else - return tag; -} - -static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) -{ - uint32_t tag; - tag = JS_VALUE_GET_TAG(v); - return tag == (JS_NAN >> 32); -} - -#else /* !JS_NAN_BOXING */ - -typedef union JSValueUnion { - int32_t int32; - double float64; - void *ptr; -} JSValueUnion; - -typedef struct JSValue { - JSValueUnion u; - int64_t tag; -} JSValue; - -#define JSValueConst JSValue - -#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag) -/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ -#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) -#define JS_VALUE_GET_INT(v) ((v).u.int32) -#define JS_VALUE_GET_BOOL(v) ((v).u.int32) -#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) -#define JS_VALUE_GET_PTR(v) ((v).u.ptr) - -// #define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } -#define JS_MKVAL(tag, val) JSValue { JSValueUnion { val }, tag } -#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } - -#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) - -#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } - -static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) -{ - JSValue v; - v.tag = JS_TAG_FLOAT64; - v.u.float64 = d; - return v; -} - -static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) -{ - union { - double d; - uint64_t u64; - } u; - if (v.tag != JS_TAG_FLOAT64) - return 0; - u.d = v.u.float64; - return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; -} - -#endif /* !JS_NAN_BOXING */ - -#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) -#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) - -#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) -#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) -#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) - -/* special values */ -#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0) -#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0) -#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0) -#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1) -#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0) -#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0) - -/* flags for object properties */ -#define JS_PROP_CONFIGURABLE (1 << 0) -#define JS_PROP_WRITABLE (1 << 1) -#define JS_PROP_ENUMERABLE (1 << 2) -#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE) -#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */ -#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */ -#define JS_PROP_NORMAL (0 << 4) -#define JS_PROP_GETSET (1 << 4) -#define JS_PROP_VARREF (2 << 4) /* used internally */ -#define JS_PROP_AUTOINIT (3 << 4) /* used internally */ - -/* flags for JS_DefineProperty */ -#define JS_PROP_HAS_SHIFT 8 -#define JS_PROP_HAS_CONFIGURABLE (1 << 8) -#define JS_PROP_HAS_WRITABLE (1 << 9) -#define JS_PROP_HAS_ENUMERABLE (1 << 10) -#define JS_PROP_HAS_GET (1 << 11) -#define JS_PROP_HAS_SET (1 << 12) -#define JS_PROP_HAS_VALUE (1 << 13) - -/* throw an exception if false would be returned - (JS_DefineProperty/JS_SetProperty) */ -#define JS_PROP_THROW (1 << 14) -/* throw an exception if false would be returned in strict mode - (JS_SetProperty) */ -#define JS_PROP_THROW_STRICT (1 << 15) - -#define JS_PROP_NO_ADD (1 << 16) /* internal use */ -#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ - -#define JS_DEFAULT_STACK_SIZE (256 * 1024) - -/* JS_Eval() flags */ -#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ -#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */ -#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */ -#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */ -#define JS_EVAL_TYPE_MASK (3 << 0) - -#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ -#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */ -/* compile but do not run. The result is an object with a - JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed - with JS_EvalFunction(). */ -#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) -/* don't include the stack frames before this eval in the Error() backtraces */ -#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) - -typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); -typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); -typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data); - -typedef struct JSMallocState { - size_t malloc_count; - size_t malloc_size; - size_t malloc_limit; - void *opaque; /* user opaque */ -} JSMallocState; - -typedef struct JSMallocFunctions { - void *(*js_malloc)(JSMallocState *s, size_t size); - void (*js_free)(JSMallocState *s, void *ptr); - void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size); - size_t (*js_malloc_usable_size)(const void *ptr); -} JSMallocFunctions; - -typedef struct JSGCObjectHeader JSGCObjectHeader; - -JSRuntime *JS_NewRuntime(void); -/* info lifetime must exceed that of rt */ -void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); -void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); -void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); -void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size); -JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque); -void JS_FreeRuntime(JSRuntime *rt); -void *JS_GetRuntimeOpaque(JSRuntime *rt); -void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque); -typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp); -void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -void JS_RunGC(JSRuntime *rt); -JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj); - -JSContext *JS_NewContext(JSRuntime *rt); -void JS_FreeContext(JSContext *s); -JSContext *JS_DupContext(JSContext *ctx); -void *JS_GetContextOpaque(JSContext *ctx); -void JS_SetContextOpaque(JSContext *ctx, void *opaque); -JSRuntime *JS_GetRuntime(JSContext *ctx); -void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj); -JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id); - -/* the following functions are used to select the intrinsic object to - save memory */ -JSContext *JS_NewContextRaw(JSRuntime *rt); -void JS_AddIntrinsicBaseObjects(JSContext *ctx); -void JS_AddIntrinsicDate(JSContext *ctx); -void JS_AddIntrinsicEval(JSContext *ctx); -void JS_AddIntrinsicStringNormalize(JSContext *ctx); -void JS_AddIntrinsicRegExpCompiler(JSContext *ctx); -void JS_AddIntrinsicRegExp(JSContext *ctx); -void JS_AddIntrinsicJSON(JSContext *ctx); -void JS_AddIntrinsicProxy(JSContext *ctx); -void JS_AddIntrinsicMapSet(JSContext *ctx); -void JS_AddIntrinsicTypedArrays(JSContext *ctx); -void JS_AddIntrinsicPromise(JSContext *ctx); -void JS_AddIntrinsicBigInt(JSContext *ctx); -void JS_AddIntrinsicBigFloat(JSContext *ctx); -void JS_AddIntrinsicBigDecimal(JSContext *ctx); -/* enable operator overloading */ -void JS_AddIntrinsicOperators(JSContext *ctx); -/* enable "use math" */ -void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable); - -JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -void *js_malloc_rt(JSRuntime *rt, size_t size); -void js_free_rt(JSRuntime *rt, void *ptr); -void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size); -size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr); -void *js_mallocz_rt(JSRuntime *rt, size_t size); - -void *js_malloc(JSContext *ctx, size_t size); -void js_free(JSContext *ctx, void *ptr); -void *js_realloc(JSContext *ctx, void *ptr, size_t size); -size_t js_malloc_usable_size(JSContext *ctx, const void *ptr); -void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack); -void *js_mallocz(JSContext *ctx, size_t size); -char *js_strdup(JSContext *ctx, const char *str); -char *js_strndup(JSContext *ctx, const char *s, size_t n); - -typedef struct JSMemoryUsage { - int64_t malloc_size, malloc_limit, memory_used_size; - int64_t malloc_count; - int64_t memory_used_count; - int64_t atom_count, atom_size; - int64_t str_count, str_size; - int64_t obj_count, obj_size; - int64_t prop_count, prop_size; - int64_t shape_count, shape_size; - int64_t js_func_count, js_func_size, js_func_code_size; - int64_t js_func_pc2line_count, js_func_pc2line_size; - int64_t c_func_count, array_count; - int64_t fast_array_count, fast_array_elements; - int64_t binary_object_count, binary_object_size; -} JSMemoryUsage; - -void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s); -void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); - -/* atom support */ -JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); -JSAtom JS_NewAtom(JSContext *ctx, const char *str); -JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n); -JSAtom JS_DupAtom(JSContext *ctx, JSAtom v); -void JS_FreeAtom(JSContext *ctx, JSAtom v); -void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); -JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); -JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); -const char *JS_AtomToCString(JSContext *ctx, JSAtom atom); -JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val); - -/* object class support */ - -typedef struct JSPropertyEnum { - JS_BOOL is_enumerable; - JSAtom atom; -} JSPropertyEnum; - -typedef struct JSPropertyDescriptor { - int flags; - JSValue value; - JSValue getter; - JSValue setter; -} JSPropertyDescriptor; - -typedef struct JSClassExoticMethods { - /* Return -1 if exception (can only happen in case of Proxy object), - FALSE if the property does not exists, TRUE if it exists. If 1 is - returned, the property descriptor 'desc' is filled if != NULL. */ - int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc, - JSValueConst obj, JSAtom prop); - /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, - -1 if exception. The 'is_enumerable' field is ignored. - */ - int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, - JSValueConst obj); - /* return < 0 if exception, or TRUE/FALSE */ - int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop); - /* return < 0 if exception or TRUE/FALSE */ - int (*define_own_property)(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags); - /* The following methods can be emulated with the previous ones, - so they are usually not needed */ - /* return < 0 if exception or TRUE/FALSE */ - int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom); - JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst receiver); - /* return < 0 if exception or TRUE/FALSE */ - int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst value, JSValueConst receiver, int flags); -} JSClassExoticMethods; - -typedef void JSClassFinalizer(JSRuntime *rt, JSValue val); -typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0) -typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_val, int argc, JSValueConst *argv, - int flags); - -typedef struct JSClassDef { - const char *class_name; - JSClassFinalizer *finalizer; - JSClassGCMark *gc_mark; - /* if call != NULL, the object is a function. If (flags & - JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a - constructor. In this case, 'this_val' is new.target. A - constructor call only happens if the object constructor bit is - set (see JS_SetConstructorBit()). */ - JSClassCall *call; - /* XXX: suppress this indirection ? It is here only to save memory - because only a few classes need these methods */ - JSClassExoticMethods *exotic; -} JSClassDef; - -JSClassID JS_NewClassID(JSClassID *pclass_id); -int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); -int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); - -/* value handling */ - -static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) -{ - return JS_MKVAL(JS_TAG_BOOL, (val != 0)); -} - -static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) -{ - return JS_MKVAL(JS_TAG_INT, val); -} - -static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) -{ - return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); -} - -static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) -{ - JSValue v; - if (val == (int32_t)val) { - v = JS_NewInt32(ctx, val); - } else { - v = __JS_NewFloat64(ctx, val); - } - return v; -} - -static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) -{ - JSValue v; - if (val <= 0x7fffffff) { - v = JS_NewInt32(ctx, val); - } else { - v = __JS_NewFloat64(ctx, val); - } - return v; -} - -JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); -JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); - -static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) -{ - JSValue v; - int32_t val; - union { - double d; - uint64_t u; - } u, t; - u.d = d; - val = (int32_t)d; - t.d = val; - /* -0 cannot be represented as integer, so we compare the bit - representation */ - if (u.u == t.u) { - v = JS_MKVAL(JS_TAG_INT, val); - } else { - v = __JS_NewFloat64(ctx, d); - } - return v; -} - -static inline JS_BOOL JS_IsNumber(JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); -} - -static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_INT; -} - -static inline JS_BOOL JS_IsBigFloat(JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_FLOAT; -} - -static inline JS_BOOL JS_IsBigDecimal(JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_DECIMAL; -} - -static inline JS_BOOL JS_IsBool(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; -} - -static inline JS_BOOL JS_IsNull(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_NULL; -} - -static inline JS_BOOL JS_IsUndefined(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; -} - -static inline JS_BOOL JS_IsException(JSValueConst v) -{ - return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION); -} - -static inline JS_BOOL JS_IsUninitialized(JSValueConst v) -{ - return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED); -} - -static inline JS_BOOL JS_IsString(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; -} - -static inline JS_BOOL JS_IsSymbol(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; -} - -static inline JS_BOOL JS_IsObject(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; -} - -JSValue JS_Throw(JSContext *ctx, JSValue obj); -JSValue JS_GetException(JSContext *ctx); -JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); -void JS_ResetUncatchableError(JSContext *ctx); -JSValue JS_NewError(JSContext *ctx); -JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); -JSValue JS_ThrowOutOfMemory(JSContext *ctx); - -void __JS_FreeValue(JSContext *ctx, JSValue v); -static inline void JS_FreeValue(JSContext *ctx, JSValue v) -{ - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); - if (--p->ref_count <= 0) { - __JS_FreeValue(ctx, v); - } - } -} -void __JS_FreeValueRT(JSRuntime *rt, JSValue v); -static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v) -{ - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); - if (--p->ref_count <= 0) { - __JS_FreeValueRT(rt, v); - } - } -} - -static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) -{ - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); - p->ref_count++; - } - return (JSValue)v; -} - -static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) -{ - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); - p->ref_count++; - } - return (JSValue)v; -} - -int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ -int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); -static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) -{ - return JS_ToInt32(ctx, (int32_t*)pres, val); -} -int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val); -int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val); -int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val); -/* return an exception if 'val' is a Number */ -int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); -/* same as JS_ToInt64() but allow BigInt */ -int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); - -JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); -JSValue JS_NewString(JSContext *ctx, const char *str); -JSValue JS_NewAtomString(JSContext *ctx, const char *str); -JSValue JS_ToString(JSContext *ctx, JSValueConst val); -JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); -const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8); -static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1) -{ - return JS_ToCStringLen2(ctx, plen, val1, 0); -} -static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1) -{ - return JS_ToCStringLen2(ctx, NULL, val1, 0); -} -void JS_FreeCString(JSContext *ctx, const char *ptr); - -JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id); -JSValue JS_NewObjectClass(JSContext *ctx, int class_id); -JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto); -JSValue JS_NewObject(JSContext *ctx); - -JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val); -JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val); -JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val); - -JSValue JS_NewArray(JSContext *ctx); -int JS_IsArray(JSContext *ctx, JSValueConst val); - -JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValueConst receiver, - JS_BOOL throw_ref_error); -static js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop) -{ - return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0); -} -JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, - const char *prop); -JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, - uint32_t idx); - -int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue val, - int flags); -static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue val) -{ - return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW); -} -int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, - uint32_t idx, JSValue val); -int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, - int64_t idx, JSValue val); -int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, - const char *prop, JSValue val); -int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop); -int JS_IsExtensible(JSContext *ctx, JSValueConst obj); -int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); -int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags); -int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val); -JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val); - -#define JS_GPN_STRING_MASK (1 << 0) -#define JS_GPN_SYMBOL_MASK (1 << 1) -#define JS_GPN_PRIVATE_MASK (1 << 2) -/* only include the enumerable properties */ -#define JS_GPN_ENUM_ONLY (1 << 4) -/* set theJSPropertyEnum.is_enumerable field */ -#define JS_GPN_SET_ENUM (1 << 5) - -int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, JSValueConst obj, int flags); -int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, - JSValueConst obj, JSAtom prop); - -JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv); -JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, - int argc, JSValueConst *argv); -JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, - int argc, JSValueConst *argv); -JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValueConst *argv); -JS_BOOL JS_DetectModule(const char *input, size_t input_len); -/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ -JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, - const char *filename, int eval_flags); -JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); -JSValue JS_GetGlobalObject(JSContext *ctx); -int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); -int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, int flags); -int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue val, int flags); -int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, - uint32_t idx, JSValue val, int flags); -int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, - const char *prop, JSValue val, int flags); -int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue getter, JSValue setter, - int flags); -void JS_SetOpaque(JSValue obj, void *opaque); -void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); -void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); - -/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ -JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename); -#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */ -JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename, int flags); -JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, - JSValueConst replacer, JSValueConst space0); - -typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); -JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, - JSFreeArrayBufferDataFunc *free_func, void *opaque, - JS_BOOL is_shared); -JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); -void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); -uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); -JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, - size_t *pbyte_offset, - size_t *pbyte_length, - size_t *pbytes_per_element); -typedef struct { - void *(*sab_alloc)(void *opaque, size_t size); - void (*sab_free)(void *opaque, void *ptr); - void (*sab_dup)(void *opaque, void *ptr); - void *sab_opaque; -} JSSharedArrayBufferFunctions; -void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, - const JSSharedArrayBufferFunctions *sf); - -JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs); - -/* is_handled = TRUE means that the rejection is handled */ -typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise, - JSValueConst reason, - JS_BOOL is_handled, void *opaque); -void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque); - -/* return != 0 if the JS code needs to be interrupted */ -typedef int JSInterruptHandler(JSRuntime *rt, void *opaque); -void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque); -/* if can_block is TRUE, Atomics.wait() can be used */ -void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block); - -typedef struct JSModuleDef JSModuleDef; - -/* return the module specifier (allocated with js_malloc()) or NULL if - exception */ -typedef char *JSModuleNormalizeFunc(JSContext *ctx, - const char *module_base_name, - const char *module_name, void *opaque); -typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx, - const char *module_name, void *opaque); - -/* module_normalize = NULL is allowed and invokes the default module - filename normalizer */ -void JS_SetModuleLoaderFunc(JSRuntime *rt, - JSModuleNormalizeFunc *module_normalize, - JSModuleLoaderFunc *module_loader, void *opaque); -/* return the import.meta object of a module */ -JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m); -JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); - -/* JS Job support */ - -typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv); -int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv); - -JS_BOOL JS_IsJobPending(JSRuntime *rt); -int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); - -/* Object Writer/Reader (currently only used to handle precompiled code) */ -#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ -#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ -#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ -#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to - encode arbitrary object - graph */ -uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, - int flags); -uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, - int flags, uint8_t ***psab_tab, size_t *psab_tab_len); - -#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */ -#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */ -#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ -#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */ -JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int flags); - -/* load the dependencies of the module 'obj'. Useful when JS_ReadObject() - returns a module. */ -int JS_ResolveModule(JSContext *ctx, JSValueConst obj); - -/* C function definition */ -typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ - JS_CFUNC_generic, - JS_CFUNC_generic_magic, - JS_CFUNC_constructor, - JS_CFUNC_constructor_magic, - JS_CFUNC_constructor_or_func, - JS_CFUNC_constructor_or_func_magic, - JS_CFUNC_f_f, - JS_CFUNC_f_f_f, - JS_CFUNC_getter, - JS_CFUNC_setter, - JS_CFUNC_getter_magic, - JS_CFUNC_setter_magic, - JS_CFUNC_iterator_next, -} JSCFunctionEnum; - -typedef union JSCFunctionType { - JSCFunction *generic; - JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); - JSCFunction *constructor; - JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic); - JSCFunction *constructor_or_func; - double (*f_f)(double); - double (*f_f_f)(double, double); - JSValue (*getter)(JSContext *ctx, JSValueConst this_val); - JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val); - JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic); - JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic); - JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int *pdone, int magic); -} JSCFunctionType; - -JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic); -JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, - int length, int magic, int data_len, - JSValueConst *data); - -static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, - int length) -{ - return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); -} - -static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic) -{ - return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); -} -void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, - JSValueConst proto); - -/* C property definition */ - -typedef struct JSCFunctionListEntry { - const char *name; - uint8_t prop_flags; - uint8_t def_type; - int16_t magic; - union { - struct { - uint8_t length; /* XXX: should move outside union */ - uint8_t cproto; /* XXX: should move outside union */ - JSCFunctionType cfunc; - } func; - struct { - JSCFunctionType get; - JSCFunctionType set; - } getset; - struct { - const char *name; - int base; - } alias; - struct { - const struct JSCFunctionListEntry *tab; - int len; - } prop_list; - const char *str; - int32_t i32; - int64_t i64; - double f64; - } u; -} JSCFunctionListEntry; - -#define JS_DEF_CFUNC 0 -#define JS_DEF_CGETSET 1 -#define JS_DEF_CGETSET_MAGIC 2 -#define JS_DEF_PROP_STRING 3 -#define JS_DEF_PROP_INT32 4 -#define JS_DEF_PROP_INT64 5 -#define JS_DEF_PROP_DOUBLE 6 -#define JS_DEF_PROP_UNDEFINED 7 -#define JS_DEF_OBJECT 8 -#define JS_DEF_ALIAS 9 - -/* Note: c++ does not like nested designators */ -#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } -#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } } -#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } } -#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } } -#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } -#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } } -#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } } -#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } } -#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } } -#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } } -#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } } -#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } } -#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } } -#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } } - -void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, - const JSCFunctionListEntry *tab, - int len); - -/* C module definition */ - -typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m); - -JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, - JSModuleInitFunc *func); -/* can only be called before the module is instantiated */ -int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str); -int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, - const JSCFunctionListEntry *tab, int len); -/* can only be called after the module is instantiated */ -int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, - JSValue val); -int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, - const JSCFunctionListEntry *tab, int len); - -#undef js_unlikely -#undef js_force_inline - -#ifdef __cplusplus -} /* extern "C" { */ -#endif - -#endif /* QUICKJS_H */ diff --git a/windows/quickjs/quickjspp.hpp b/windows/quickjs/quickjspp.hpp deleted file mode 100644 index 72e2b18..0000000 --- a/windows/quickjs/quickjspp.hpp +++ /dev/null @@ -1,1304 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace qjs { -#include "quickjs/quickjs.h" -#include "quickjs/quickjs-libc.h" - - -/** Exception type. - * Indicates that exception has occured in JS context. - */ -class exception {}; - -/** Javascript conversion traits. - * Describes how to convert type R to/from JSValue. Second template argument can be used for SFINAE/enable_if type filters. - */ -template -struct js_traits -{ - /** Create an object of C++ type R given JSValue v and JSContext. - * This function is intentionally not implemented. User should implement this function for their own type. - * @param v This value is passed as JSValueConst so it should be freed by the caller. - * @throws exception in case of conversion error - */ - static R unwrap(JSContext * ctx, JSValueConst v); - /** Create JSValue from an object of type R and JSContext. - * This function is intentionally not implemented. User should implement this function for their own type. - * @return Returns JSValue which should be freed by the caller or JS_EXCEPTION in case of error. - */ - static JSValue wrap(JSContext * ctx, R value); -}; - -/** Conversion traits for JSValue (identity). - */ -template <> -struct js_traits -{ - static JSValue unwrap(JSContext * ctx, JSValueConst v) noexcept - { - return JS_DupValue(ctx, v); - } - - static JSValue wrap(JSContext * ctx, JSValue v) noexcept - { - return v; - } -}; - -/** Conversion traits for integers. - */ -template -struct js_traits && sizeof(Int) <= sizeof(int64_t)>> -{ - - /// @throws exception - static Int unwrap(JSContext * ctx, JSValueConst v) - { - if constexpr (sizeof(Int) > sizeof(int32_t)) - { - int64_t r; - if(JS_ToInt64(ctx, &r, v)) - throw exception{}; - return static_cast(r); - } - else - { - int32_t r; - if(JS_ToInt32(ctx, &r, v)) - throw exception{}; - return static_cast(r); - } - } - - static JSValue wrap(JSContext * ctx, Int i) noexcept - { - if constexpr (std::is_same_v || sizeof(Int) > sizeof(int32_t)) - return JS_NewInt64(ctx, static_cast(i)); - else - return JS_NewInt32(ctx, static_cast(i)); - } -}; - -/** Conversion traits for boolean. - */ -template <> -struct js_traits -{ - static bool unwrap(JSContext * ctx, JSValueConst v) noexcept - { - return JS_ToBool(ctx, v); - } - - static JSValue wrap(JSContext * ctx, bool i) noexcept - { - return JS_NewBool(ctx, i); - } -}; - -/** Conversion trait for void. - */ -template <> -struct js_traits -{ - /// @throws exception if jsvalue is neither undefined nor null - static void unwrap(JSContext * ctx, JSValueConst value) - { - if(JS_IsException(value)) - throw exception{}; - } -}; - -/** Conversion traits for float64/double. - */ -template <> -struct js_traits -{ - /// @throws exception - static double unwrap(JSContext * ctx, JSValueConst v) - { - double r; - if(JS_ToFloat64(ctx, &r, v)) - throw exception{}; - return r; - } - - static JSValue wrap(JSContext * ctx, double i) noexcept - { - return JS_NewFloat64(ctx, i); - } -}; - -namespace detail { -/** Fake std::string_view which frees the string on destruction. -*/ -class js_string : public std::string_view -{ - using Base = std::string_view; - JSContext * ctx = nullptr; - - friend struct js_traits; - - js_string(JSContext * ctx, const char * ptr, std::size_t len) : Base(ptr, len), ctx(ctx) - {} - -public: - - template - js_string(Args&& ... args) : Base(std::forward(args)...), ctx(nullptr) - {} - - js_string(const js_string& other) = delete; - - operator const char * () const { - return this->data(); - } - - ~js_string() - { - if(ctx) - JS_FreeCString(ctx, this->data()); - } -}; -} // namespace detail - -/** Conversion traits from std::string_view and to detail::js_string. */ -template <> -struct js_traits -{ - static detail::js_string unwrap(JSContext * ctx, JSValueConst v) - { - size_t plen; - const char * ptr = JS_ToCStringLen(ctx, &plen, v); - if(!ptr) - throw exception{}; - return detail::js_string{ctx, ptr, plen}; - } - - static JSValue wrap(JSContext * ctx, std::string_view str) noexcept - { - return JS_NewStringLen(ctx, str.data(), str.size()); - } -}; - -/** Conversion traits for std::string */ -template <> // slower -struct js_traits -{ - static std::string unwrap(JSContext * ctx, JSValueConst v) - { - auto str_view = js_traits::unwrap(ctx, v); - return std::string{str_view.data(), str_view.size()}; - } - - static JSValue wrap(JSContext * ctx, const std::string& str) noexcept - { - return JS_NewStringLen(ctx, str.data(), str.size()); - } -}; - -/** Conversion from const char * */ -template <> -struct js_traits -{ - static JSValue wrap(JSContext * ctx, const char * str) noexcept - { - return JS_NewString(ctx, str); - } - static detail::js_string unwrap(JSContext * ctx, JSValueConst v) - { - return js_traits::unwrap(ctx, v); - } -}; - - -namespace detail { - -/** Helper function to convert and then free JSValue. */ -template -T unwrap_free(JSContext * ctx, JSValue val) -{ - if constexpr(std::is_same_v) - { - JS_FreeValue(ctx, val); - return js_traits::unwrap(ctx, val); - } else - { - try - { - T result = js_traits>::unwrap(ctx, val); - JS_FreeValue(ctx, val); - return result; - } - catch(...) - { - JS_FreeValue(ctx, val); - throw; - } - } -} - -template -Tuple unwrap_args_impl(JSContext * ctx, JSValueConst * argv, std::index_sequence) -{ - return Tuple{js_traits>>::unwrap(ctx, argv[I])...}; -} - -/** Helper function to convert an array of JSValues to a tuple. - * @tparam Args C++ types of the argv array - */ -template -std::tuple...> unwrap_args(JSContext * ctx, JSValueConst * argv) -{ - return unwrap_args_impl...>>(ctx, argv, std::make_index_sequence()); -} - -/** Helper function to call f with an array of JSValues. - * @tparam R return type of f - * @tparam Args argument types of f - * @tparam Callable type of f (inferred) - * @param ctx JSContext - * @param f callable object - * @param argv array of JSValue's - * @return converted return value of f or JS_NULL if f returns void - */ -template -JSValue wrap_call(JSContext * ctx, Callable&& f, JSValueConst * argv) noexcept -{ - try - { - if constexpr(std::is_same_v) - { - std::apply(std::forward(f), unwrap_args(ctx, argv)); - return JS_NULL; - } else - { - return js_traits>::wrap(ctx, - std::apply(std::forward(f), - unwrap_args(ctx, argv))); - } - } - catch(exception) - { - return JS_EXCEPTION; - } -} - -/** Same as wrap_call, but pass this_value as first argument. - * @tparam FirstArg type of this_value - */ -template -JSValue wrap_this_call(JSContext * ctx, Callable&& f, JSValueConst this_value, JSValueConst * argv) noexcept -{ - try - { - if constexpr(std::is_same_v) - { - std::apply(std::forward(f), std::tuple_cat(unwrap_args(ctx, &this_value), - unwrap_args(ctx, argv))); - return JS_NULL; - } else - { - return js_traits>::wrap(ctx, - std::apply(std::forward(f), - std::tuple_cat( - unwrap_args(ctx, &this_value), - unwrap_args(ctx, argv)))); - } - } - catch(exception) - { - return JS_EXCEPTION; - } -} - -template -void wrap_args_impl(JSContext * ctx, JSValue * argv, Tuple tuple, std::index_sequence) -{ - ((argv[I] = js_traits>>::wrap(ctx, std::get(tuple))), ...); -} - -/** Converts C++ args to JSValue array. - * @tparam Args argument types - * @param argv array of size at least sizeof...(Args) - */ -template -void wrap_args(JSContext * ctx, JSValue * argv, Args&& ... args) -{ - wrap_args_impl(ctx, argv, std::make_tuple(std::forward(args)...), - std::make_index_sequence()); -} -} // namespace detail - -/** A wrapper type for free and class member functions. - * Pointer to function F is a template argument. - * @tparam F either a pointer to free function or a pointer to class member function - * @tparam PassThis if true and F is a pointer to free function, passes Javascript "this" value as first argument: - */ -template -struct fwrapper -{ - /// "name" property of the JS function object (not defined if nullptr) - const char * name = nullptr; -}; - -/** Conversion to JSValue for free function in fwrapper. */ -template -struct js_traits> -{ - static JSValue wrap(JSContext * ctx, fwrapper fw) noexcept - { - return JS_NewCFunction(ctx, [](JSContext * ctx, JSValueConst this_value, int argc, - JSValueConst * argv) noexcept -> JSValue { - if constexpr(PassThis) - return detail::wrap_this_call(ctx, F, this_value, argv); - else - return detail::wrap_call(ctx, F, argv); - }, fw.name, sizeof...(Args)); - - } -}; - -/** Conversion to JSValue for class member function in fwrapper. */ -template -struct js_traits> -{ - static JSValue wrap(JSContext * ctx, fwrapper fw) noexcept - { - return JS_NewCFunction(ctx, [](JSContext * ctx, JSValueConst this_value, int argc, - JSValueConst * argv) noexcept -> JSValue { - return detail::wrap_this_call, Args...>(ctx, F, this_value, argv); - }, fw.name, sizeof...(Args)); - - } -}; - -/** Conversion to JSValue for const class member function in fwrapper. */ -template -struct js_traits> -{ - static JSValue wrap(JSContext * ctx, fwrapper fw) noexcept - { - return JS_NewCFunction(ctx, [](JSContext * ctx, JSValueConst this_value, int argc, - JSValueConst * argv) noexcept -> JSValue { - return detail::wrap_this_call, Args...>(ctx, F, this_value, argv); - }, fw.name, sizeof...(Args)); - - } -}; - -/** A wrapper type for constructor of type T with arguments Args. - * Compilation fails if no such constructor is defined. - * @tparam Args constructor arguments - */ -template -struct ctor_wrapper -{ - static_assert(std::is_constructible::value, "no such constructor!"); - /// "name" property of JS constructor object - const char * name = nullptr; -}; - -/** Conversion to JSValue for ctor_wrapper. */ -template -struct js_traits> -{ - static JSValue wrap(JSContext * ctx, ctor_wrapper cw) noexcept - { - return JS_NewCFunction2(ctx, [](JSContext * ctx, JSValueConst this_value, int argc, - JSValueConst * argv) noexcept -> JSValue { - - if(js_traits>::QJSClassId == 0) // not registered - { -#if defined(__cpp_rtti) - // automatically register class on first use (no prototype) - js_traits>::register_class(ctx, typeid(T).name()); -#else - JS_ThrowTypeError(ctx, "quickjspp ctor_wrapper::wrap: Class is not registered"); - return JS_EXCEPTION; -#endif - } - - auto proto = JS_GetPropertyStr(ctx, this_value, "prototype"); - if (JS_IsException(proto)) - return proto; - auto jsobj = JS_NewObjectProtoClass(ctx, proto, js_traits>::QJSClassId); - JS_FreeValue(ctx, proto); - if (JS_IsException(jsobj)) - return jsobj; - - std::shared_ptr ptr = std::apply(std::make_shared, detail::unwrap_args(ctx, argv)); - JS_SetOpaque(jsobj, new std::shared_ptr(std::move(ptr))); - return jsobj; - - // return detail::wrap_call, Args...>(ctx, std::make_shared, argv); - }, cw.name, sizeof...(Args), JS_CFUNC_constructor, 0); - } -}; - - -/** Conversions for std::shared_ptr. - * T should be registered to a context before conversions. - * @tparam T class type - */ -template -struct js_traits> -{ - /// Registered class id in QuickJS. - inline static JSClassID QJSClassId = 0; - - /** Register class in QuickJS context. - * - * @param ctx context - * @param name class name - * @param proto class prototype or JS_NULL - * @throws exception - */ - static void register_class(JSContext * ctx, const char * name, JSValue proto = JS_NULL) - { - if(QJSClassId == 0) - { - JS_NewClassID(&QJSClassId); - } - auto rt = JS_GetRuntime(ctx); - if(!JS_IsRegisteredClass(rt, QJSClassId)) - { - JSClassDef def{ - name, - // destructor - [](JSRuntime * rt, JSValue obj) noexcept { - auto pptr = reinterpret_cast *>(JS_GetOpaque(obj, QJSClassId)); - delete pptr; - } - }; - int e = JS_NewClass(rt, QJSClassId, &def); - if(e < 0) - { - JS_ThrowInternalError(ctx, "Cant register class %s", name); - throw exception{}; - } - } - JS_SetClassProto(ctx, QJSClassId, proto); - } - - /** Create a JSValue from std::shared_ptr. - * Creates an object with class if #QJSClassId and sets its opaque pointer to a new copy of #ptr. - */ - static JSValue wrap(JSContext * ctx, std::shared_ptr ptr) - { - if(QJSClassId == 0) // not registered - { -#if defined(__cpp_rtti) - // automatically register class on first use (no prototype) - register_class(ctx, typeid(T).name()); -#else - JS_ThrowTypeError(ctx, "quickjspp std::shared_ptr::wrap: Class is not registered"); - return JS_EXCEPTION; -#endif - } - auto jsobj = JS_NewObjectClass(ctx, QJSClassId); - if(JS_IsException(jsobj)) - return jsobj; - - auto pptr = new std::shared_ptr(std::move(ptr)); - JS_SetOpaque(jsobj, pptr); - return jsobj; - } - - /// @throws exception if #v doesn't have the correct class id - static const std::shared_ptr& unwrap(JSContext * ctx, JSValueConst v) - { - auto ptr = reinterpret_cast *>(JS_GetOpaque2(ctx, v, QJSClassId)); - if(!ptr) - throw exception{}; - return *ptr; - } -}; - -// T * - non-owning pointer -template -struct js_traits -{ - static JSValue wrap(JSContext * ctx, T * ptr) - { - if(js_traits>::QJSClassId == 0) // not registered - { -#if defined(__cpp_rtti) - js_traits>::register_class(ctx, typeid(T).name()); -#else - JS_ThrowTypeError(ctx, "quickjspp js_traits::wrap: Class is not registered"); - return JS_EXCEPTION; -#endif - } - auto jsobj = JS_NewObjectClass(ctx, js_traits>::QJSClassId); - if(JS_IsException(jsobj)) - return jsobj; - - // shared_ptr with empty deleter since we don't own T* - auto pptr = new std::shared_ptr(ptr, [](T *){}); - JS_SetOpaque(jsobj, pptr); - return jsobj; - } - - static T * unwrap(JSContext * ctx, JSValueConst v) - { - auto ptr = reinterpret_cast *>(JS_GetOpaque2(ctx, v, - js_traits>::QJSClassId)); - if(!ptr) - throw exception{}; - return ptr->get(); - } -}; - -namespace detail { -/** A faster std::function-like object with type erasure. - * Used to convert any callable objects (including lambdas) to JSValue. - */ -struct function -{ - JSValue - (* invoker)(function * self, JSContext * ctx, JSValueConst this_value, int argc, JSValueConst * argv) = nullptr; - - void (* destroyer)(function * self) = nullptr; - - alignas(std::max_align_t) char functor[]; - - template - static function * create(JSRuntime * rt, Functor&& f) - { - auto fptr = reinterpret_cast(js_malloc_rt(rt, sizeof(function) + sizeof(Functor))); - if(!fptr) - throw std::bad_alloc{}; - new(fptr) function; - auto functorptr = reinterpret_cast(fptr->functor); - new(functorptr) Functor(std::forward(f)); - fptr->destroyer = nullptr; - if constexpr(!std::is_trivially_destructible_v) - { - fptr->destroyer = [](function * fptr) { - auto functorptr = reinterpret_cast(fptr->functor); - functorptr->~Functor(); - }; - } - return fptr; - } -}; - -static_assert(std::is_trivially_destructible_v); -} - -template <> -struct js_traits -{ - inline static JSClassID QJSClassId = 0; - - // TODO: replace ctx with rt - static void register_class(JSContext * ctx, const char * name) - { - if(QJSClassId == 0) - { - JS_NewClassID(&QJSClassId); - } - auto rt = JS_GetRuntime(ctx); - if(JS_IsRegisteredClass(rt, QJSClassId)) - return; - JSClassDef def{ - name, - // destructor - [](JSRuntime * rt, JSValue obj) noexcept { - auto fptr = reinterpret_cast(JS_GetOpaque(obj, QJSClassId)); - assert(fptr); - if(fptr->destroyer) - fptr->destroyer(fptr); - js_free_rt(rt, fptr); - }, - nullptr, // mark - // call - [](JSContext * ctx, JSValueConst func_obj, JSValueConst this_val, int argc, - JSValueConst * argv, int flags) -> JSValue { - auto ptr = reinterpret_cast(JS_GetOpaque2(ctx, func_obj, QJSClassId)); - if(!ptr) - return JS_EXCEPTION; - return ptr->invoker(ptr, ctx, this_val, argc, argv); - } - }; - int e = JS_NewClass(rt, QJSClassId, &def); - if(e < 0) - throw std::runtime_error{"Cannot register C++ function class"}; - } -}; - - -/** Traits for accessing object properties. - * @tparam Key property key type (uint32 and strings are supported) - */ -template -struct js_property_traits -{ - static void set_property(JSContext * ctx, JSValue this_obj, Key key, JSValue value); - static JSValue get_property(JSContext * ctx, JSValue this_obj, Key key); -}; - -template <> -struct js_property_traits -{ - static void set_property(JSContext * ctx, JSValue this_obj, const char * name, JSValue value) - { - int err = JS_SetPropertyStr(ctx, this_obj, name, value); - if(err < 0) - throw exception{}; - } - - static JSValue get_property(JSContext * ctx, JSValue this_obj, const char * name) noexcept - { - return JS_GetPropertyStr(ctx, this_obj, name); - } -}; - -template <> -struct js_property_traits -{ - static void set_property(JSContext * ctx, JSValue this_obj, uint32_t idx, JSValue value) - { - int err = JS_SetPropertyUint32(ctx, this_obj, idx, value); - if(err < 0) - throw exception{}; - } - - static JSValue get_property(JSContext * ctx, JSValue this_obj, uint32_t idx) noexcept - { - return JS_GetPropertyUint32(ctx, this_obj, idx); - } -}; - -class Value; - -namespace detail { -template -struct property_proxy -{ - JSContext * ctx; - JSValue this_obj; - Key key; - - /** Conversion helper function */ - template - T as() const - { - return unwrap_free(ctx, js_property_traits::get_property(ctx, this_obj, key)); - } - - /** Explicit conversion operator (to any type) */ - template - explicit operator T() const { return as(); } - - /** Implicit converion to qjs::Value */ - operator Value() const; // defined later due to Value being incomplete type - - template - property_proxy& operator =(Value value) - { - js_property_traits::set_property(ctx, this_obj, key, - js_traits::wrap(ctx, std::move(value))); - return *this; - } -}; - - -// class member variable getter/setter -template -struct get_set {}; - -template -struct get_set -{ - using is_const = std::is_const; - - static const R& get(const std::shared_ptr& ptr) - { - return *ptr.*M; - } - - static R& set(const std::shared_ptr& ptr, R value) - { - return *ptr.*M = std::move(value); - } - -}; - -} // namespace detail - -/** JSValue with RAAI semantics. - * A wrapper over (JSValue v, JSContext * ctx). - * Calls JS_FreeValue(ctx, v) on destruction. Can be copied and moved. - * A JSValue can be released by either JSValue x = std::move(value); or JSValue x = value.release(), then the Value becomes invalid and FreeValue won't be called - * Can be converted to C++ type, for example: auto string = value.as(); qjs::exception would be thrown on error - * Properties can be accessed (read/write): value["property1"] = 1; value[2] = "2"; - */ -class Value -{ -public: - JSValue v; - JSContext * ctx = nullptr; - -public: - /** Use context.newValue(val) instead */ - template - Value(JSContext * ctx, T&& val) : ctx(ctx) - { - v = js_traits>::wrap(ctx, std::forward(val)); - if(JS_IsException(v)) - throw exception{}; - } - - Value(const Value& rhs) - { - ctx = rhs.ctx; - v = JS_DupValue(ctx, rhs.v); - } - - Value(Value&& rhs) - { - std::swap(ctx, rhs.ctx); - v = rhs.v; - } - - Value& operator=(Value rhs) - { - std::swap(ctx, rhs.ctx); - std::swap(v, rhs.v); - return *this; - } - - bool operator==(JSValueConst other) const - { - return JS_VALUE_GET_TAG(v) == JS_VALUE_GET_TAG(other) && JS_VALUE_GET_PTR(v) == JS_VALUE_GET_PTR(other); - } - - bool operator!=(JSValueConst other) const { return !((*this) == other); } - - - /** Returns true if 2 values are the same (equality for arithmetic types or point to the same object) */ - bool operator==(const Value& rhs) const - { - return ctx == rhs.ctx && (*this == rhs.v); - } - - bool operator!=(const Value& rhs) const { return !((*this) == rhs); } - - - ~Value() - { - if(ctx) JS_FreeValue(ctx, v); - } - - bool isError() const { return JS_IsError(ctx, v); } - - /** Conversion helper function. Both value.as() and static_cast(value) are supported */ - template - T as() const { return js_traits>::unwrap(ctx, v); } - - /** Explicit conversion to any type */ - template - explicit operator T() const { return as(); } - - JSValue release() // dont call freevalue - { - ctx = nullptr; - return v; - } - - /** Implicit conversion to JSValue (rvalue only). Example: JSValue v = std::move(value); */ - operator JSValue() && { return release(); } - - - /** Access JS properties. Returns proxy type which is implicitly convertible to qjs::Value */ - template - detail::property_proxy operator [](Key key) - { - return {ctx, v, std::move(key)}; - } - - - // add("f", []() {...}); - template - Value& add(const char * name, Function&& f) - { - (*this)[name] = js_traits(f)})>::wrap(ctx, - std::forward(f)); - return *this; - } - - // add<&f>("f"); - // add<&T::f>("f"); - template - std::enable_if_t, Value&> - add(const char * name) - { - (*this)[name] = fwrapper{name}; - return *this; - } - - // add<&T::member>("member"); - template - std::enable_if_t, Value&> - add(const char * name) - { - auto prop = JS_NewAtom(ctx, name); - using fgetter = fwrapper::get, true>; - int ret; - if constexpr (detail::get_set::is_const::value) - { - ret = JS_DefinePropertyGetSet(ctx, v, prop, - js_traits::wrap(ctx, fgetter{name}), - JS_UNDEFINED, - JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE - ); - } else - { - using fsetter = fwrapper::set, true>; - ret = JS_DefinePropertyGetSet(ctx, v, prop, - js_traits::wrap(ctx, fgetter{name}), - js_traits::wrap(ctx, fsetter{name}), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE - ); - } - JS_FreeAtom(ctx, prop); - if(ret < 0) - throw exception{}; - return *this; - } - - std::string toJSON(const Value& replacer = Value{nullptr, JS_UNDEFINED}, const Value& space = Value{nullptr, JS_UNDEFINED}) - { - assert(ctx); - assert(!replacer.ctx || ctx == replacer.ctx); - assert(!space.ctx || ctx == space.ctx); - JSValue json = JS_JSONStringify(ctx, v, replacer.v, space.v); - return (std::string)Value{ctx, json}; - } - -}; - -/** Thin wrapper over JSRuntime * rt - * Calls JS_FreeRuntime on destruction. noncopyable. - */ -class Runtime -{ -public: - JSRuntime * rt; - - Runtime() - { - rt = JS_NewRuntime(); - if(!rt) - throw std::runtime_error{"qjs: Cannot create runtime"}; - } - - // noncopyable - Runtime(const Runtime&) = delete; - - ~Runtime() - { - JS_FreeRuntime(rt); - } -}; - -/** Wrapper over JSContext * ctx - * Calls JS_SetContextOpaque(ctx, this); on construction and JS_FreeContext on destruction - */ -class Context -{ -public: - JSContext * ctx; - - /** Module wrapper - * Workaround for lack of opaque pointer for module load function by keeping a list of modules in qjs::Context. - */ - class Module - { - friend class Context; - - JSModuleDef * m; - JSContext * ctx; - const char * name; - - using nvp = std::pair; - std::vector exports; - public: - Module(JSContext * ctx, const char * name) : ctx(ctx), name(name) - { - m = JS_NewCModule(ctx, name, [](JSContext * ctx, JSModuleDef * m) noexcept { - auto& context = Context::get(ctx); - auto it = std::find_if(context.modules.begin(), context.modules.end(), - [m](const Module& module) { return module.m == m; }); - if(it == context.modules.end()) - return -1; - for(const auto& e : it->exports) - { - if(JS_SetModuleExport(ctx, m, e.first, JS_DupValue(ctx, e.second.v)) != 0) - return -1; - } - return 0; - }); - if(!m) - throw exception{}; - } - - Module& add(const char * name, JSValue value) - { - exports.push_back({name, {ctx, value}}); - JS_AddModuleExport(ctx, m, name); - return *this; - } - - Module& add(const char * name, Value value) - { - assert(value.ctx == ctx); - exports.push_back({name, std::move(value)}); - JS_AddModuleExport(ctx, m, name); - return *this; - } - - template - Module& add(const char * name, T value) - { - return add(name, js_traits::wrap(ctx, std::move(value))); - } - - Module(const Module&) = delete; - - Module(Module&&) = default; - //Module& operator=(Module&&) = default; - - - // function wrappers - - /** Add free function F. - * Example: - * module.function(&::sin)>("sin"); - */ - template - Module& function(const char * name) - { - return add(name, qjs::fwrapper{name}); - } - - /** Add function object f. - * Slower than template version. - * Example: module.function("sin", [](double x) { return ::sin(x); }); - */ - template - Module& function(const char * name, F&& f) - { - return add(name, js_traits(f)})>::wrap(std::forward(f))); - } - - // class register wrapper - private: - /** Helper class to register class members and constructors. - * See fun, constructor. - * Actual registration occurs at object destruction. - */ - template - class class_registrar - { - const char * name; - qjs::Value prototype; - qjs::Context::Module& module; - qjs::Context& context; - public: - explicit class_registrar(const char * name, qjs::Context::Module& module, qjs::Context& context) : - name(name), - prototype(context.newObject()), - module(module), - context(context) - { - } - - class_registrar(const class_registrar&) = delete; - - /** Add functional object f - */ - template - class_registrar& fun(const char * name, F&& f) - { - prototype.add(name, std::forward(f)); - return *this; - } - - /** Add class member function or class member variable F - * Example: - * struct T { int var; int func(); } - * auto& module = context.addModule("module"); - * module.class_("T").fun<&T::var>("var").fun<&T::func>("func"); - */ - template - class_registrar& fun(const char * name) - { - prototype.add(name); - return *this; - } - - /** Add class constructor - * @tparam Args contructor arguments - * @param name constructor name (if not specified class name will be used) - */ - template - class_registrar& constructor(const char * name = nullptr) - { - if(!name) - name = this->name; - Value ctor = context.newValue(qjs::ctor_wrapper{name}); - JS_SetConstructor(context.ctx, ctor.v, prototype.v); - module.add(name, std::move(ctor)); - return *this; - } - - /* TODO: needs casting to base class - template - class_registrar& base() - { - assert(js_traits>::QJSClassId && "base class is not registered"); - auto base_proto = JS_GetClassProto(context.ctx, js_traits>::QJSClassId); - int err = JS_SetPrototype(context.ctx, prototype.v, base_proto); - JS_FreeValue(context.ctx, base_proto); - if(err < 0) - throw exception{}; - return *this; - } - */ - - ~class_registrar() - { - context.registerClass(name, std::move(prototype)); - } - }; - - public: - /** Add class to module. - * See \ref class_registrar. - */ - template - class_registrar class_(const char * name) - { - return class_registrar{name, *this, qjs::Context::get(ctx)}; - } - - }; - - std::vector modules; -private: - void init() - { - JS_SetContextOpaque(ctx, this); - js_traits::register_class(ctx, "C++ function"); - } - -public: - Context(Runtime& rt) : Context(rt.rt) - {} - - Context(JSRuntime * rt) - { - ctx = JS_NewContext(rt); - if(!ctx) - throw std::runtime_error{"qjs: Cannot create context"}; - init(); - } - - Context(JSContext * ctx) : ctx{ctx} - { - init(); - } - - // noncopyable - Context(const Context&) = delete; - - ~Context() - { - modules.clear(); - JS_FreeContext(ctx); - } - - /** Create module and return a reference to it */ - Module& addModule(const char * name) - { - modules.emplace_back(ctx, name); - return modules.back(); - } - - /** returns globalThis */ - Value global() { return Value{ctx, JS_GetGlobalObject(ctx)}; } - - /** returns new Object() */ - Value newObject() { return Value{ctx, JS_NewObject(ctx)}; } - - /** returns JS value converted from c++ object val */ - template - Value newValue(T&& val) { return Value{ctx, std::forward(val)}; } - - /** returns current exception associated with context, and resets it. Should be called when qjs::exception is caught */ - Value getException() { return Value{ctx, JS_GetException(ctx)}; } - - /** Register class T for conversions to/from std::shared_ptr to work. - * Wherever possible module.class_("T")... should be used instead. - * @tparam T class type - * @param name class name in JS engine - * @param proto JS class prototype or JS_UNDEFINED - */ - template - void registerClass(const char * name, JSValue proto = JS_NULL) - { - js_traits>::register_class(ctx, name, proto); - } - - Value eval(std::string_view buffer, const char * filename = "", unsigned eval_flags = 0) - { - assert(buffer.data()[buffer.size()] == '\0' && "eval buffer is not null-terminated"); // JS_Eval requirement - JSValue v = JS_Eval(ctx, buffer.data(), buffer.size(), filename, eval_flags); - return Value{ctx, v}; - } - - Value evalFile(const char * filename, unsigned eval_flags = 0) - { - size_t buf_len; - auto deleter = [this](void * p) { js_free(ctx, p); }; - auto buf = std::unique_ptr{js_load_file(ctx, &buf_len, filename), deleter}; - if(!buf) - throw std::runtime_error{std::string{"evalFile: can't read file: "} + filename}; - return eval({reinterpret_cast(buf.get()), buf_len}, filename, eval_flags); - } - - Value fromJSON(std::string_view buffer, const char * filename = "") - { - assert(buffer.data()[buffer.size()] == '\0' && "fromJSON buffer is not null-terminated"); // JS_ParseJSON requirement - JSValue v = JS_ParseJSON(ctx, buffer.data(), buffer.size(), filename); - return Value{ctx, v}; - } - - /** Get qjs::Context from JSContext opaque pointer */ - static Context& get(JSContext * ctx) - { - void * ptr = JS_GetContextOpaque(ctx); - assert(ptr); - return *reinterpret_cast(ptr); - } -}; - -/** Conversion traits for Value. - */ -template <> -struct js_traits -{ - static Value unwrap(JSContext * ctx, JSValueConst v) - { - return Value{ctx, JS_DupValue(ctx, v)}; - } - - static JSValue wrap(JSContext * ctx, Value v) noexcept - { - assert(ctx == v.ctx); - return v.release(); - } -}; - -/** Convert to/from std::function - * @tparam R return type - * @tparam Args argument types - */ -template -struct js_traits> -{ - static std::function unwrap(JSContext * ctx, JSValueConst fun_obj) - { - return [jsfun_obj = Value{ctx, JS_DupValue(ctx, fun_obj)}](Args&& ... args) -> R { - const int argc = sizeof...(Args); - JSValue argv[argc]; - detail::wrap_args(jsfun_obj.ctx, argv, std::forward(args)...); - JSValue result = JS_Call(jsfun_obj.ctx, jsfun_obj.v, JS_UNDEFINED, argc, const_cast(argv)); - for(int i = 0; i < argc; i++) JS_FreeValue(jsfun_obj.ctx, argv[i]); - return detail::unwrap_free(jsfun_obj.ctx, result); - }; - } - - /** Convert from function object functor to JSValue. - * Uses detail::function for type-erasure. - */ - template - static JSValue wrap(JSContext * ctx, Functor&& functor) - { - using detail::function; - assert(js_traits::QJSClassId); - auto obj = JS_NewObjectClass(ctx, js_traits::QJSClassId); - if(JS_IsException(obj)) - return JS_EXCEPTION; - auto fptr = function::create(JS_GetRuntime(ctx), std::forward(functor)); - fptr->invoker = [](function * self, JSContext * ctx, JSValueConst this_value, int argc, JSValueConst * argv) { - assert(self); - auto f = reinterpret_cast(&self->functor); - return detail::wrap_call(ctx, *f, argv); - }; - JS_SetOpaque(obj, fptr); - return obj; - } -}; - -/** Convert from std::vector to Array and vice-versa. If Array holds objects that are non-convertible to T throws qjs::exception */ -template -struct js_traits> -{ - static JSValue wrap(JSContext * ctx, const std::vector& arr) noexcept - { - auto jsarray = Value{ctx, JS_NewArray(ctx)}; - if(JS_IsException(jsarray.v)) - return jsarray.v; - - try - { - for(uint32_t i = 0; i < (uint32_t) arr.size(); i++) - jsarray[i] = arr[i]; - } - catch(exception) - { - return JS_EXCEPTION; - } - return std::move(jsarray); - } - - static std::vector unwrap(JSContext * ctx, JSValueConst jsarr) - { - int e = JS_IsArray(ctx, jsarr); - if(e == 0) - JS_ThrowTypeError(ctx, "js_traits>::unwrap expects array"); - if(e <= 0) - throw exception{}; - Value jsarray{ctx, JS_DupValue(ctx, jsarr)}; - std::vector arr; - auto len = static_cast(jsarray["length"]); - arr.reserve((uint32_t) len); - for(uint32_t i = 0; i < (uint32_t) len; i++) - arr.push_back(static_cast(jsarray[i])); - return arr; - } -}; - -namespace detail { -template -property_proxy::operator Value() const -{ - return as(); -} -} - -} // namespace qjs \ No newline at end of file