Diff
checker
文本
文本
图像
文档
Excel
文件夹
Legal
Enterprise
桌面版
定价
登录
下载 Diffchecker 桌面版
比较文本
查找两个文本文件之间的差异
工具
历史
实时编辑器
隐藏空白更改
折叠未更改行
关闭换行
视图
拆分
统一
比对精度
智能
单词
字符
文本样式
更改外观
语法高亮
选择语法
忽略
文本转换
转到第一个差异
编辑输入
Diffchecker Desktop
运行Diffchecker最安全的方式。获取Diffchecker桌面应用:您的差异永远不会离开您的电脑!
获取桌面版
txiki.js vm.c FFI support removed
创建于
10个月前
差异永不过期
清除
导出
分享
解释
0 删除
行
总计
删除
字符
总计
删除
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
607 行
全部复制
2 添加
行
总计
添加
字符
总计
添加
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
608 行
全部复制
/*
/*
* txiki.js
* txiki.js
*
*
* Copyright (c) 2019-present Saúl Ibarra Corretgé <s@saghul.net>
* Copyright (c) 2019-present Saúl Ibarra Corretgé <s@saghul.net>
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* 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,
* 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
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* THE SOFTWARE.
*/
*/
#include "mem.h"
#include "mem.h"
#include "private.h"
#include "private.h"
#include "tjs.h"
#include "tjs.h"
#include <signal.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#define TJS__DEFAULT_STACK_SIZE 1024 * 1024 // 1 MB
#define TJS__DEFAULT_STACK_SIZE 1024 * 1024 // 1 MB
/* JS malloc functions */
/* JS malloc functions */
static void *tjs__mf_calloc(void *opaque, size_t count, size_t size) {
static void *tjs__mf_calloc(void *opaque, size_t count, size_t size) {
(void) opaque;
(void) opaque;
return tjs__calloc(count, size);
return tjs__calloc(count, size);
}
}
static void *tjs__mf_malloc(void *opaque, size_t size) {
static void *tjs__mf_malloc(void *opaque, size_t size) {
(void) opaque;
(void) opaque;
return tjs__malloc(size);
return tjs__malloc(size);
}
}
static void tjs__mf_free(void *opaque, void *ptr) {
static void tjs__mf_free(void *opaque, void *ptr) {
(void) opaque;
(void) opaque;
tjs__free(ptr);
tjs__free(ptr);
}
}
static void *tjs__mf_realloc(void *opaque, void *ptr, size_t size) {
static void *tjs__mf_realloc(void *opaque, void *ptr, size_t size) {
(void) opaque;
(void) opaque;
return tjs__realloc(ptr, size);
return tjs__realloc(ptr, size);
}
}
static const JSMallocFunctions tjs_mf = {
static const JSMallocFunctions tjs_mf = {
.js_calloc = tjs__mf_calloc,
.js_calloc = tjs__mf_calloc,
.js_malloc = tjs__mf_malloc,
.js_malloc = tjs__mf_malloc,
.js_free = tjs__mf_free,
.js_free = tjs__mf_free,
.js_realloc = tjs__mf_realloc,
.js_realloc = tjs__mf_realloc,
.js_malloc_usable_size = tjs__malloc_usable_size,
.js_malloc_usable_size = tjs__malloc_usable_size,
};
};
/* SharedArrayBuffer functions */
/* SharedArrayBuffer functions */
typedef struct {
typedef struct {
int ref_count;
int ref_count;
uint8_t buf[0];
uint8_t buf[0];
} TJSSABHeader;
} TJSSABHeader;
static int atomic_add_int(int *ptr, int v) {
static int atomic_add_int(int *ptr, int v) {
return atomic_fetch_add((_Atomic(uint32_t) *) ptr, v) + v;
return atomic_fetch_add((_Atomic(uint32_t) *) ptr, v) + v;
}
}
static void *tjs__sab_alloc(void *opaque, size_t size) {
static void *tjs__sab_alloc(void *opaque, size_t size) {
TJSSABHeader *sab = tjs__malloc(sizeof(*sab) + size);
TJSSABHeader *sab = tjs__malloc(sizeof(*sab) + size);
if (!sab) {
if (!sab) {
return NULL;
return NULL;
}
}
sab->ref_count = 1;
sab->ref_count = 1;
return sab->buf;
return sab->buf;
}
}
void tjs__sab_free(void *opaque, void *ptr) {
void tjs__sab_free(void *opaque, void *ptr) {
TJSSABHeader *sab = (TJSSABHeader *) ((uint8_t *) ptr - sizeof(TJSSABHeader));
TJSSABHeader *sab = (TJSSABHeader *) ((uint8_t *) ptr - sizeof(TJSSABHeader));
int ref_count = atomic_add_int(&sab->ref_count, -1);
int ref_count = atomic_add_int(&sab->ref_count, -1);
assert(ref_count >= 0);
assert(ref_count >= 0);
if (ref_count == 0) {
if (ref_count == 0) {
tjs__free(sab);
tjs__free(sab);
}
}
}
}
void tjs__sab_dup(void *opaque, void *ptr) {
void tjs__sab_dup(void *opaque, void *ptr) {
TJSSABHeader *sab = (TJSSABHeader *) ((uint8_t *) ptr - sizeof(TJSSABHeader));
TJSSABHeader *sab = (TJSSABHeader *) ((uint8_t *) ptr - sizeof(TJSSABHeader));
atomic_add_int(&sab->ref_count, 1);
atomic_add_int(&sab->ref_count, 1);
}
}
static const JSSharedArrayBufferFunctions tjs_sf = {
static const JSSharedArrayBufferFunctions tjs_sf = {
.sab_alloc = tjs__sab_alloc,
.sab_alloc = tjs__sab_alloc,
.sab_dup = tjs__sab_dup,
.sab_dup = tjs__sab_dup,
.sab_free = tjs__sab_free,
.sab_free = tjs__sab_free,
.sab_opaque = NULL,
.sab_opaque = NULL,
};
};
/* core */
/* core */
extern const uint8_t tjs__core[];
extern const uint8_t tjs__core[];
extern const uint32_t tjs__core_size;
extern const uint32_t tjs__core_size;
extern const uint8_t tjs__polyfills[];
extern const uint8_t tjs__polyfills[];
extern const uint32_t tjs__polyfills_size;
extern const uint32_t tjs__polyfills_size;
extern const uint8_t tjs__run_main[];
extern const uint8_t tjs__run_main[];
extern const uint32_t tjs__run_main_size;
extern const uint32_t tjs__run_main_size;
static int tjs__argc = 0;
static int tjs__argc = 0;
static char **tjs__argv = NULL;
static char **tjs__argv = NULL;
static void tjs__bootstrap_core(JSContext *ctx, JSValue ns) {
static void tjs__bootstrap_core(JSContext *ctx, JSValue ns) {
tjs__mod_dns_init(ctx, ns);
tjs__mod_dns_init(ctx, ns);
tjs__mod_engine_init(ctx, ns);
tjs__mod_engine_init(ctx, ns);
tjs__mod_error_init(ctx, ns);
tjs__mod_error_init(ctx, ns);
复制
已复制
复制
已复制
tjs__mod_ffi_init(ctx, ns);
//
tjs__mod_ffi_init(ctx, ns);
tjs__mod_fs_init(ctx, ns);
tjs__mod_fs_init(ctx, ns);
tjs__mod_fswatch_init(ctx, ns);
tjs__mod_fswatch_init(ctx, ns);
tjs__mod_os_init(ctx, ns);
tjs__mod_os_init(ctx, ns);
tjs__mod_process_init(ctx, ns);
tjs__mod_process_init(ctx, ns);
tjs__mod_signals_init(ctx, ns);
tjs__mod_signals_init(ctx, ns);
tjs__mod_sqlite3_init(ctx, ns);
tjs__mod_sqlite3_init(ctx, ns);
tjs__mod_streams_init(ctx, ns);
tjs__mod_streams_init(ctx, ns);
tjs__mod_sys_init(ctx, ns);
tjs__mod_sys_init(ctx, ns);
tjs__mod_timers_init(ctx, ns);
tjs__mod_timers_init(ctx, ns);
tjs__mod_udp_init(ctx, ns);
tjs__mod_udp_init(ctx, ns);
tjs__mod_wasm_init(ctx, ns);
tjs__mod_wasm_init(ctx, ns);
tjs__mod_worker_init(ctx, ns);
tjs__mod_worker_init(ctx, ns);
tjs__mod_ws_init(ctx, ns);
tjs__mod_ws_init(ctx, ns);
tjs__mod_xhr_init(ctx, ns);
tjs__mod_xhr_init(ctx, ns);
#ifndef _WIN32
#ifndef _WIN32
tjs__mod_posix_socket_init(ctx, ns);
tjs__mod_posix_socket_init(ctx, ns);
#endif
#endif
}
}
JSValue tjs__get_args(JSContext *ctx) {
JSValue tjs__get_args(JSContext *ctx) {
JSValue args = JS_NewArray(ctx);
JSValue args = JS_NewArray(ctx);
for (int i = 0; i < tjs__argc; i++) {
for (int i = 0; i < tjs__argc; i++) {
JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, tjs__argv[i]));
JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, tjs__argv[i]));
}
}
return args;
return args;
}
}
static JSValue tjs__dispatch_event(JSContext *ctx, JSValue *event) {
static JSValue tjs__dispatch_event(JSContext *ctx, JSValue *event) {
TJSRuntime *qrt = TJS_GetRuntime(ctx);
TJSRuntime *qrt = TJS_GetRuntime(ctx);
CHECK_NOT_NULL(qrt);
CHECK_NOT_NULL(qrt);
if (qrt->freeing) {
if (qrt->freeing) {
return JS_UNDEFINED;
return JS_UNDEFINED;
}
}
JSValue global_obj = JS_GetGlobalObject(ctx);
JSValue global_obj = JS_GetGlobalObject(ctx);
JSValue ret = JS_Call(ctx, qrt->builtins.dispatch_event_func, global_obj, 1, event);
JSValue ret = JS_Call(ctx, qrt->builtins.dispatch_event_func, global_obj, 1, event);
JS_FreeValue(ctx, global_obj);
JS_FreeValue(ctx, global_obj);
return ret;
return ret;
}
}
static void tjs__promise_rejection_tracker(JSContext *ctx,
static void tjs__promise_rejection_tracker(JSContext *ctx,
JSValue promise,
JSValue promise,
JSValue reason,
JSValue reason,
bool is_handled,
bool is_handled,
void *opaque) {
void *opaque) {
TJSRuntime *qrt = TJS_GetRuntime(ctx);
TJSRuntime *qrt = TJS_GetRuntime(ctx);
CHECK_NOT_NULL(qrt);
CHECK_NOT_NULL(qrt);
if (qrt->freeing) {
if (qrt->freeing) {
return;
return;
}
}
if (!is_handled) {
if (!is_handled) {
JSValue event_name = JS_NewString(ctx, "unhandledrejection");
JSValue event_name = JS_NewString(ctx, "unhandledrejection");
JSValue args[3];
JSValue args[3];
args[0] = event_name;
args[0] = event_name;
args[1] = promise;
args[1] = promise;
args[2] = reason;
args[2] = reason;
JSValue event = JS_CallConstructor(ctx, qrt->builtins.promise_event_ctor, countof(args), args);
JSValue event = JS_CallConstructor(ctx, qrt->builtins.promise_event_ctor, countof(args), args);
CHECK_EQ(JS_IsException(event), 0);
CHECK_EQ(JS_IsException(event), 0);
JSValue ret = tjs__dispatch_event(ctx, &event);
JSValue ret = tjs__dispatch_event(ctx, &event);
JS_FreeValue(ctx, event);
JS_FreeValue(ctx, event);
JS_FreeValue(ctx, event_name);
JS_FreeValue(ctx, event_name);
if (JS_IsException(ret)) {
if (JS_IsException(ret)) {
tjs_dump_error(ctx);
tjs_dump_error(ctx);
goto fail;
goto fail;
} else {
} else {
if (JS_ToBool(ctx, ret)) {
if (JS_ToBool(ctx, ret)) {
// The event wasn't cancelled, maybe abort.
// The event wasn't cancelled, maybe abort.
fail:;
fail:;
TJSRuntime *qrt = TJS_GetRuntime(ctx);
TJSRuntime *qrt = TJS_GetRuntime(ctx);
CHECK_NOT_NULL(qrt);
CHECK_NOT_NULL(qrt);
JS_Throw(qrt->ctx, JS_DupValue(qrt->ctx, reason));
JS_Throw(qrt->ctx, JS_DupValue(qrt->ctx, reason));
TJS_Stop(qrt);
TJS_Stop(qrt);
}
}
}
}
JS_FreeValue(ctx, ret);
JS_FreeValue(ctx, ret);
}
}
}
}
static void uv__stop(uv_async_t *handle) {
static void uv__stop(uv_async_t *handle) {
TJSRuntime *qrt = handle->data;
TJSRuntime *qrt = handle->data;
CHECK_NOT_NULL(qrt);
CHECK_NOT_NULL(qrt);
uv_stop(&qrt->loop);
uv_stop(&qrt->loop);
}
}
void TJS_DefaultOptions(TJSRunOptions *options) {
void TJS_DefaultOptions(TJSRunOptions *options) {
static TJSRunOptions default_options = { .mem_limit = 0, .stack_size = TJS__DEFAULT_STACK_SIZE };
static TJSRunOptions default_options = { .mem_limit = 0, .stack_size = TJS__DEFAULT_STACK_SIZE };
memcpy(options, &default_options, sizeof(*options));
memcpy(options, &default_options, sizeof(*options));
}
}
TJSRuntime *TJS_NewRuntime(void) {
TJSRuntime *TJS_NewRuntime(void) {
TJSRunOptions options;
TJSRunOptions options;
TJS_DefaultOptions(&options);
TJS_DefaultOptions(&options);
return TJS_NewRuntimeInternal(false, &options);
return TJS_NewRuntimeInternal(false, &options);
}
}
TJSRuntime *TJS_NewRuntimeOptions(TJSRunOptions *options) {
TJSRuntime *TJS_NewRuntimeOptions(TJSRunOptions *options) {
return TJS_NewRuntimeInternal(false, options);
return TJS_NewRuntimeInternal(false, options);
}
}
TJSRuntime *TJS_NewRuntimeWorker(void) {
TJSRuntime *TJS_NewRuntimeWorker(void) {
TJSRunOptions options;
TJSRunOptions options;
TJS_DefaultOptions(&options);
TJS_DefaultOptions(&options);
return TJS_NewRuntimeInternal(true, &options);
return TJS_NewRuntimeInternal(true, &options);
}
}
TJSRuntime *TJS_NewRuntimeInternal(bool is_worker, TJSRunOptions *options) {
TJSRuntime *TJS_NewRuntimeInternal(bool is_worker, TJSRunOptions *options) {
JSRuntime *rt = NULL;
JSRuntime *rt = NULL;
JSContext *ctx = NULL;
JSContext *ctx = NULL;
TJSRuntime *qrt = tjs__mallocz(sizeof(*qrt));
TJSRuntime *qrt = tjs__mallocz(sizeof(*qrt));
memcpy(&qrt->options, options, sizeof(*options));
memcpy(&qrt->options, options, sizeof(*options));
rt = JS_NewRuntime2(&tjs_mf, NULL);
rt = JS_NewRuntime2(&tjs_mf, NULL);
CHECK_NOT_NULL(rt);
CHECK_NOT_NULL(rt);
qrt->rt = rt;
qrt->rt = rt;
ctx = JS_NewContext(rt);
ctx = JS_NewContext(rt);
CHECK_NOT_NULL(ctx);
CHECK_NOT_NULL(ctx);
qrt->ctx = ctx;
qrt->ctx = ctx;
JS_SetRuntimeOpaque(rt, qrt);
JS_SetRuntimeOpaque(rt, qrt);
JS_SetContextOpaque(ctx, qrt);
JS_SetContextOpaque(ctx, qrt);
/* Set memory limit */
/* Set memory limit */
JS_SetMemoryLimit(rt, options->mem_limit);
JS_SetMemoryLimit(rt, options->mem_limit);
/* Set stack size */
/* Set stack size */
JS_SetMaxStackSize(rt, options->stack_size);
JS_SetMaxStackSize(rt, options->stack_size);
/* SharedArrayBuffer functions */
/* SharedArrayBuffer functions */
JS_SetSharedArrayBufferFunctions(rt, &tjs_sf);
JS_SetSharedArrayBufferFunctions(rt, &tjs_sf);
/* Worker support */
/* Worker support */
qrt->is_worker = is_worker;
qrt->is_worker = is_worker;
JS_SetCanBlock(rt, is_worker);
JS_SetCanBlock(rt, is_worker);
CHECK_EQ(uv_loop_init(&qrt->loop), 0);
CHECK_EQ(uv_loop_init(&qrt->loop), 0);
/* handle which runs the job queue */
/* handle which runs the job queue */
CHECK_EQ(uv_prepare_init(&qrt->loop, &qrt->jobs.prepare), 0);
CHECK_EQ(uv_prepare_init(&qrt->loop, &qrt->jobs.prepare), 0);
qrt->jobs.prepare.data = qrt;
qrt->jobs.prepare.data = qrt;
/* handle to prevent the loop from blocking for i/o when there are pending jobs. */
/* handle to prevent the loop from blocking for i/o when there are pending jobs. */
CHECK_EQ(uv_idle_init(&qrt->loop, &qrt->jobs.idle), 0);
CHECK_EQ(uv_idle_init(&qrt->loop, &qrt->jobs.idle), 0);
qrt->jobs.idle.data = qrt;
qrt->jobs.idle.data = qrt;
/* handle which runs the job queue */
/* handle which runs the job queue */
CHECK_EQ(uv_check_init(&qrt->loop, &qrt->jobs.check), 0);
CHECK_EQ(uv_check_init(&qrt->loop, &qrt->jobs.check), 0);
qrt->jobs.check.data = qrt;
qrt->jobs.check.data = qrt;
/* handle for stopping this runtime (also works from another thread) */
/* handle for stopping this runtime (also works from another thread) */
CHECK_EQ(uv_async_init(&qrt->loop, &qrt->stop, uv__stop), 0);
CHECK_EQ(uv_async_init(&qrt->loop, &qrt->stop, uv__stop), 0);
qrt->stop.data = qrt;
qrt->stop.data = qrt;
/* loader for ES modules */
/* loader for ES modules */
JS_SetModuleLoaderFunc(rt, tjs_module_normalizer, tjs_module_loader, qrt);
JS_SetModuleLoaderFunc(rt, tjs_module_normalizer, tjs_module_loader, qrt);
/* unhandled promise rejection tracker */
/* unhandled promise rejection tracker */
JS_SetHostPromiseRejectionTracker(rt, tjs__promise_rejection_tracker, NULL);
JS_SetHostPromiseRejectionTracker(rt, tjs__promise_rejection_tracker, NULL);
/* start bootstrap */
/* start bootstrap */
JSValue global_obj = JS_GetGlobalObject(ctx);
JSValue global_obj = JS_GetGlobalObject(ctx);
JSValue core_sym = JS_NewSymbol(ctx, "tjs.internal.core", true);
JSValue core_sym = JS_NewSymbol(ctx, "tjs.internal.core", true);
JSAtom core_atom = JS_ValueToAtom(ctx, core_sym);
JSAtom core_atom = JS_ValueToAtom(ctx, core_sym);
JSValue core = JS_NewObjectProto(ctx, JS_NULL);
JSValue core = JS_NewObjectProto(ctx, JS_NULL);
CHECK_EQ(JS_DefinePropertyValue(ctx, global_obj, core_atom, core, JS_PROP_C_W_E), true);
CHECK_EQ(JS_DefinePropertyValue(ctx, global_obj, core_atom, core, JS_PROP_C_W_E), true);
CHECK_EQ(JS_DefinePropertyValueStr(ctx, core, "isWorker", JS_NewBool(ctx, is_worker), JS_PROP_C_W_E), true);
CHECK_EQ(JS_DefinePropertyValueStr(ctx, core, "isWorker", JS_NewBool(ctx, is_worker), JS_PROP_C_W_E), true);
tjs__bootstrap_core(ctx, core);
tjs__bootstrap_core(ctx, core);
CHECK_EQ(tjs__eval_bytecode(ctx, tjs__polyfills, tjs__polyfills_size, true), 0);
CHECK_EQ(tjs__eval_bytecode(ctx, tjs__polyfills, tjs__polyfills_size, true), 0);
CHECK_EQ(tjs__eval_bytecode(ctx, tjs__core, tjs__core_size, true), 0);
CHECK_EQ(tjs__eval_bytecode(ctx, tjs__core, tjs__core_size, true), 0);
/* Load some builtin references for easy access */
/* Load some builtin references for easy access */
qrt->builtins.dispatch_event_func = JS_GetPropertyStr(ctx, global_obj, "dispatchEvent");
qrt->builtins.dispatch_event_func = JS_GetPropertyStr(ctx, global_obj, "dispatchEvent");
CHECK_EQ(JS_IsUndefined(qrt->builtins.dispatch_event_func), 0);
CHECK_EQ(JS_IsUndefined(qrt->builtins.dispatch_event_func), 0);
qrt->builtins.promise_event_ctor = JS_GetPropertyStr(qrt->ctx, global_obj, "PromiseRejectionEvent");
qrt->builtins.promise_event_ctor = JS_GetPropertyStr(qrt->ctx, global_obj, "PromiseRejectionEvent");
CHECK_EQ(JS_IsUndefined(qrt->builtins.promise_event_ctor), 0);
CHECK_EQ(JS_IsUndefined(qrt->builtins.promise_event_ctor), 0);
/* end bootstrap */
/* end bootstrap */
JS_FreeAtom(ctx, core_atom);
JS_FreeAtom(ctx, core_atom);
JS_FreeValue(ctx, core_sym);
JS_FreeValue(ctx, core_sym);
JS_FreeValue(ctx, global_obj);
JS_FreeValue(ctx, global_obj);
/* WASM */
/* WASM */
qrt->wasm_ctx.env = m3_NewEnvironment();
qrt->wasm_ctx.env = m3_NewEnvironment();
/* Timers */
/* Timers */
qrt->timers.timers = NULL;
qrt->timers.timers = NULL;
qrt->timers.next_timer = 1;
qrt->timers.next_timer = 1;
return qrt;
return qrt;
}
}
void TJS_FreeRuntime(TJSRuntime *qrt) {
void TJS_FreeRuntime(TJSRuntime *qrt) {
qrt->freeing = true;
qrt->freeing = true;
/* Close all core loop handles. */
/* Close all core loop handles. */
uv_close((uv_handle_t *) &qrt->jobs.prepare, NULL);
uv_close((uv_handle_t *) &qrt->jobs.prepare, NULL);
uv_close((uv_handle_t *) &qrt->jobs.idle, NULL);
uv_close((uv_handle_t *) &qrt->jobs.idle, NULL);
uv_close((uv_handle_t *) &qrt->jobs.check, NULL);
uv_close((uv_handle_t *) &qrt->jobs.check, NULL);
uv_close((uv_handle_t *) &qrt->stop, NULL);
uv_close((uv_handle_t *) &qrt->stop, NULL);
if (qrt->curl_ctx.curlm_h) {
if (qrt->curl_ctx.curlm_h) {
uv_close((uv_handle_t *) &qrt->curl_ctx.timer, NULL);
uv_close((uv_handle_t *) &qrt->curl_ctx.timer, NULL);
}
}
/* Destroy all timers */
/* Destroy all timers */
tjs__destroy_timers(qrt);
tjs__destroy_timers(qrt);
/* Destroy the JS engine. */
/* Destroy the JS engine. */
JS_FreeValue(qrt->ctx, qrt->builtins.dispatch_event_func);
JS_FreeValue(qrt->ctx, qrt->builtins.dispatch_event_func);
qrt->builtins.dispatch_event_func = JS_UNDEFINED;
qrt->builtins.dispatch_event_func = JS_UNDEFINED;
JS_FreeValue(qrt->ctx, qrt->builtins.promise_event_ctor);
JS_FreeValue(qrt->ctx, qrt->builtins.promise_event_ctor);
qrt->builtins.promise_event_ctor = JS_UNDEFINED;
qrt->builtins.promise_event_ctor = JS_UNDEFINED;
JS_FreeContext(qrt->ctx);
JS_FreeContext(qrt->ctx);
JS_FreeRuntime(qrt->rt);
JS_FreeRuntime(qrt->rt);
/* Destroy CURLM handle. */
/* Destroy CURLM handle. */
if (qrt->curl_ctx.curlm_h) {
if (qrt->curl_ctx.curlm_h) {
curl_multi_cleanup(qrt->curl_ctx.curlm_h);
curl_multi_cleanup(qrt->curl_ctx.curlm_h);
qrt->curl_ctx.curlm_h = NULL;
qrt->curl_ctx.curlm_h = NULL;
}
}
/* Destroy WASM runtime. */
/* Destroy WASM runtime. */
m3_FreeEnvironment(qrt->wasm_ctx.env);
m3_FreeEnvironment(qrt->wasm_ctx.env);
qrt->wasm_ctx.env = NULL;
qrt->wasm_ctx.env = NULL;
/* Cleanup loop. All handles should be closed. */
/* Cleanup loop. All handles should be closed. */
int closed = 0;
int closed = 0;
for (int i = 0; i < 5; i++) {
for (int i = 0; i < 5; i++) {
if (uv_loop_close(&qrt->loop) == 0) {
if (uv_loop_close(&qrt->loop) == 0) {
closed = 1;
closed = 1;
break;
break;
}
}
uv_run(&qrt->loop, UV_RUN_NOWAIT);
uv_run(&qrt->loop, UV_RUN_NOWAIT);
}
}
#ifdef DEBUG
#ifdef DEBUG
if (!closed) {
if (!closed) {
uv_print_all_handles(&qrt->loop, stderr);
uv_print_all_handles(&qrt->loop, stderr);
}
}
CHECK_EQ(closed, 1);
CHECK_EQ(closed, 1);
#else
#else
(void) closed;
(void) closed;
#endif
#endif
tjs__free(qrt);
tjs__free(qrt);
}
}
void TJS_Initialize(int argc, char **argv) {
void TJS_Initialize(int argc, char **argv) {
curl_global_init(CURL_GLOBAL_ALL);
curl_global_init(CURL_GLOBAL_ALL);
CHECK_EQ(0, uv_replace_allocator(tjs__malloc, tjs__realloc, tjs__calloc, tjs__free));
CHECK_EQ(0, uv_replace_allocator(tjs__malloc, tjs__realloc, tjs__calloc, tjs__free));
tjs__argc = argc;
tjs__argc = argc;
tjs__argv = uv_setup_args(argc, argv);
tjs__argv = uv_setup_args(argc, argv);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
#ifdef SIGPIPE
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
#endif
#endif
}
}
JSContext *TJS_GetJSContext(TJSRuntime *qrt) {
JSContext *TJS_GetJSContext(TJSRuntime *qrt) {
return qrt->ctx;
return qrt->ctx;
}
}
TJSRuntime *TJS_GetRuntime(JSContext *ctx) {
TJSRuntime *TJS_GetRuntime(JSContext *ctx) {
return JS_GetContextOpaque(ctx);
return JS_GetContextOpaque(ctx);
}
}
static void uv__idle_cb(uv_idle_t *handle) {
static void uv__idle_cb(uv_idle_t *handle) {
// Noop
// Noop
}
}
static void uv__maybe_idle(TJSRuntime *qrt) {
static void uv__maybe_idle(TJSRuntime *qrt) {
if (JS_IsJobPending(qrt->rt)) {
if (JS_IsJobPending(qrt->rt)) {
CHECK_EQ(uv_idle_start(&qrt->jobs.idle, uv__idle_cb), 0);
CHECK_EQ(uv_idle_start(&qrt->jobs.idle, uv__idle_cb), 0);
} else {
} else {
CHECK_EQ(uv_idle_stop(&qrt->jobs.idle), 0);
CHECK_EQ(uv_idle_stop(&qrt->jobs.idle), 0);
}
}
}
}
static void uv__prepare_cb(uv_prepare_t *handle) {
static void uv__prepare_cb(uv_prepare_t *handle) {
TJSRuntime *qrt = handle->data;
TJSRuntime *qrt = handle->data;
CHECK_NOT_NULL(qrt);
CHECK_NOT_NULL(qrt);
uv__maybe_idle(qrt);
uv__maybe_idle(qrt);
}
}
void tjs__execute_jobs(JSContext *ctx) {
void tjs__execute_jobs(JSContext *ctx) {
JSContext *ctx1;
JSContext *ctx1;
int err;
int err;
/* execute the pending jobs */
/* execute the pending jobs */
for (;;) {
for (;;) {
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
if (err <= 0) {
if (err <= 0) {
if (err < 0) {
if (err < 0) {
TJSRuntime *qrt = TJS_GetRuntime(ctx);
TJSRuntime *qrt = TJS_GetRuntime(ctx);
CHECK_NOT_NULL(qrt);
CHECK_NOT_NULL(qrt);
TJS_Stop(qrt);
TJS_Stop(qrt);
}
}
break;
break;
}
}
}
}
}
}
static void uv__check_cb(uv_check_t *handle) {
static void uv__check_cb(uv_check_t *handle) {
TJSRuntime *qrt = handle->data;
TJSRuntime *qrt = handle->data;
CHECK_NOT_NULL(qrt);
CHECK_NOT_NULL(qrt);
tjs__execute_jobs(qrt->ctx);
tjs__execute_jobs(qrt->ctx);
uv__maybe_idle(qrt);
uv__maybe_idle(qrt);
}
}
/* main loop which calls the user JS callbacks */
/* main loop which calls the user JS callbacks */
int TJS_Run(TJSRuntime *qrt) {
int TJS_Run(TJSRuntime *qrt) {
int ret = 0;
int ret = 0;
CHECK_EQ(uv_prepare_start(&qrt->jobs.prepare, uv__prepare_cb), 0);
CHECK_EQ(uv_prepare_start(&qrt->jobs.prepare, uv__prepare_cb), 0);
uv_unref((uv_handle_t *) &qrt->jobs.prepare);
uv_unref((uv_handle_t *) &qrt->jobs.prepare);
CHECK_EQ(uv_check_start(&qrt->jobs.check, uv__check_cb), 0);
CHECK_EQ(uv_check_start(&qrt->jobs.check, uv__check_cb), 0);
uv_unref((uv_handle_t *) &qrt->jobs.check);
uv_unref((uv_handle_t *) &qrt->jobs.check);
/* Use the async handle to keep the worker alive even when there is nothing to do. */
/* Use the async handle to keep the worker alive even when there is nothing to do. */
if (!qrt->is_worker) {
if (!qrt->is_worker) {
uv_unref((uv_handle_t *) &qrt->stop);
uv_unref((uv_handle_t *) &qrt->stop);
/* If we are running the main interpreter, run the entrypoint. */
/* If we are running the main interpreter, run the entrypoint. */
ret = tjs__eval_bytecode(qrt->ctx, tjs__run_main, tjs__run_main_size, true);
ret = tjs__eval_bytecode(qrt->ctx, tjs__run_main, tjs__run_main_size, true);
}
}
if (ret != 0) {
if (ret != 0) {
return ret;
return ret;
}
}
int r;
int r;
do {
do {
uv__maybe_idle(qrt);
uv__maybe_idle(qrt);
r = uv_run(&qrt->loop, UV_RUN_DEFAULT);
r = uv_run(&qrt->loop, UV_RUN_DEFAULT);
} while (r == 0 && JS_IsJobPending(qrt->rt));
} while (r == 0 && JS_IsJobPending(qrt->rt));
if (JS_HasException(qrt->ctx)) {
if (JS_HasException(qrt->ctx)) {
tjs_dump_error(qrt->ctx);
tjs_dump_error(qrt->ctx);
ret = 1;
ret = 1;
}
}
return ret;
return ret;
}
}
void TJS_Stop(TJSRuntime *qrt) {
void TJS_Stop(TJSRuntime *qrt) {
CHECK_NOT_NULL(qrt);
CHECK_NOT_NULL(qrt);
uv_async_send(&qrt->stop);
uv_async_send(&qrt->stop);
}
}
uv_loop_t *TJS_GetLoop(TJSRuntime *qrt) {
uv_loop_t *TJS_GetLoop(TJSRuntime *qrt) {
return &qrt->loop;
return &qrt->loop;
}
}
int tjs__load_file(JSContext *ctx, DynBuf *dbuf, const char *filename) {
int tjs__load_file(JSContext *ctx, DynBuf *dbuf, const char *filename) {
uv_fs_t req;
uv_fs_t req;
uv_file fd;
uv_file fd;
int r;
int r;
r = uv_fs_open(NULL, &req, filename, O_RDONLY, 0, NULL);
r = uv_fs_open(NULL, &req, filename, O_RDONLY, 0, NULL);
uv_fs_req_cleanup(&req);
uv_fs_req_cleanup(&req);
if (r < 0) {
if (r < 0) {
return r;
return r;
}
}
fd = r;
fd = r;
char buf[64 * 1024];
char buf[64 * 1024];
uv_buf_t b = uv_buf_init(buf, sizeof(buf));
uv_buf_t b = uv_buf_init(buf, sizeof(buf));
size_t offset = 0;
size_t offset = 0;
do {
do {
r = uv_fs_read(NULL, &req, fd, &b, 1, offset, NULL);
r = uv_fs_read(NULL, &req, fd, &b, 1, offset, NULL);
uv_fs_req_cleanup(&req);
uv_fs_req_cleanup(&req);
if (r <= 0) {
if (r <= 0) {
break;
break;
}
}
offset += r;
offset += r;
r = dbuf_put(dbuf, (const uint8_t *) b.base, r);
r = dbuf_put(dbuf, (const uint8_t *) b.base, r);
if (r != 0) {
if (r != 0) {
break;
break;
}
}
} while (1);
} while (1);
uv_fs_close(NULL, &req, fd, NULL);
uv_fs_close(NULL, &req, fd, NULL);
uv_fs_req_cleanup(&req);
uv_fs_req_cleanup(&req);
return r;
return r;
}
}
JSValue TJS_EvalModuleContent(JSContext *ctx,
JSValue TJS_EvalModuleContent(JSContext *ctx,
const char *specifier,
const char *specifier,
bool is_main,
bool is_main,
bool use_real_path,
bool use_real_path,
const char *content,
const char *content,
size_t len) {
size_t len) {
/* Compile then run to be able to set import.meta */
/* Compile then run to be able to set import.meta */
JSValue ret = JS_Eval(ctx, content, len, specifier, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
JSValue ret = JS_Eval(ctx, content, len, specifier, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
if (!JS_IsException(ret)) {
if (!JS_IsException(ret)) {
js_module_set_import_meta(ctx, ret, use_real_path, is_main);
js_module_set_import_meta(ctx, ret, use_real_path, is_main);
ret = JS_EvalFunction(ctx, ret);
ret = JS_EvalFunction(ctx, ret);
}
}
/* Emit window 'load' event. */
/* Emit window 'load' event. */
if (!JS_IsException(ret) && is_main) {
if (!JS_IsException(ret) && is_main) {
static char emit_window_load[] = "window.dispatchEvent(new Event('load'));";
static char emit_window_load[] = "window.dispatchEvent(new Event('load'));";
JSValue ret1 = JS_Eval(ctx, emit_window_load, strlen(emit_window_load), "<global>", JS_EVAL_TYPE_GLOBAL);
JSValue ret1 = JS_Eval(ctx, emit_window_load, strlen(emit_window_load), "<global>", JS_EVAL_TYPE_GLOBAL);
if (JS_IsException(ret1)) {
if (JS_IsException(ret1)) {
tjs_dump_error(ctx);
tjs_dump_error(ctx);
}
}
}
}
return ret;
return ret;
}
}
JSValue TJS_EvalScript(JSContext *ctx, const char *filename) {
JSValue TJS_EvalScript(JSContext *ctx, const char *filename) {
DynBuf dbuf;
DynBuf dbuf;
size_t dbuf_size;
size_t dbuf_size;
int r;
int r;
JSValue ret;
JSValue ret;
tjs_dbuf_init(ctx, &dbuf);
tjs_dbuf_init(ctx, &dbuf);
r = tjs__load_file(ctx, &dbuf, filename);
r = tjs__load_file(ctx, &dbuf, filename);
if (r != 0) {
if (r != 0) {
dbuf_free(&dbuf);
dbuf_free(&dbuf);
JS_ThrowReferenceError(ctx, "could not load '%s' - %s: %s", filename, uv_err_name(r), uv_strerror(r));
JS_ThrowReferenceError(ctx, "could not load '%s' - %s: %s", filename, uv_err_name(r), uv_strerror(r));
return JS_EXCEPTION;
return JS_EXCEPTION;
}
}
dbuf_size = dbuf.size;
dbuf_size = dbuf.size;
/* Add null termination, required by JS_Eval. */
/* Add null termination, required by JS_Eval. */
dbuf_putc(&dbuf, '\0');
dbuf_putc(&dbuf, '\0');
ret = JS_Eval(ctx, (char *) dbuf.buf, dbuf_size - 1, filename, JS_EVAL_TYPE_GLOBAL);
ret = JS_Eval(ctx, (char *) dbuf.buf, dbuf_size - 1, filename, JS_EVAL_TYPE_GLOBAL);
dbuf_free(&dbuf);
dbuf_free(&dbuf);
return ret;
return ret;
}
}
JSValue TJS_EvalModule(JSContext *ctx, const char *filename, bool is_main) {
JSValue TJS_EvalModule(JSContext *ctx, const char *filename, bool is_main) {
DynBuf dbuf;
DynBuf dbuf;
size_t dbuf_size;
size_t dbuf_size;
int r;
int r;
JSValue ret;
JSValue ret;
tjs_dbuf_init(ctx, &dbuf);
tjs_dbuf_init(ctx, &dbuf);
r = tjs__load_file(ctx, &dbuf, filename);
r = tjs__load_file(ctx, &dbuf, filename);
if (r != 0) {
if (r != 0) {
dbuf_free(&dbuf);
dbuf_free(&dbuf);
JS_ThrowReferenceError(ctx, "could not load '%s' - %s: %s", filename, uv_err_name(r), uv_strerror(r));
JS_ThrowReferenceError(ctx, "could not load '%s' - %s: %s", filename, uv_err_name(r), uv_strerror(r));
return JS_EXCEPTION;
return JS_EXCEPTION;
}
}
dbuf_size = dbuf.size;
dbuf_size = dbuf.size;
/* Add null termination, required by JS_Eval. */
/* Add null termination, required by JS_Eval. */
dbuf_putc(&dbuf, '\0');
dbuf_putc(&dbuf, '\0');
ret = TJS_EvalModuleContent(ctx, filename, is_main, true, (char *) dbuf.buf, dbuf_size - 1);
ret = TJS_EvalModuleContent(ctx, filename, is_main, true, (char *) dbuf.buf, dbuf_size - 1);
dbuf_free(&dbuf);
dbuf_free(&dbuf);
return ret;
return ret;
}
}
复制
已复制
复制
已复制
已保存差异
原始文本
打开文件
/* * txiki.js * * Copyright (c) 2019-present Saúl Ibarra Corretgé <s@saghul.net> * * 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. */ #include "mem.h" #include "private.h" #include "tjs.h" #include <signal.h> #include <stdatomic.h> #include <stdio.h> #include <string.h> #define TJS__DEFAULT_STACK_SIZE 1024 * 1024 // 1 MB /* JS malloc functions */ static void *tjs__mf_calloc(void *opaque, size_t count, size_t size) { (void) opaque; return tjs__calloc(count, size); } static void *tjs__mf_malloc(void *opaque, size_t size) { (void) opaque; return tjs__malloc(size); } static void tjs__mf_free(void *opaque, void *ptr) { (void) opaque; tjs__free(ptr); } static void *tjs__mf_realloc(void *opaque, void *ptr, size_t size) { (void) opaque; return tjs__realloc(ptr, size); } static const JSMallocFunctions tjs_mf = { .js_calloc = tjs__mf_calloc, .js_malloc = tjs__mf_malloc, .js_free = tjs__mf_free, .js_realloc = tjs__mf_realloc, .js_malloc_usable_size = tjs__malloc_usable_size, }; /* SharedArrayBuffer functions */ typedef struct { int ref_count; uint8_t buf[0]; } TJSSABHeader; static int atomic_add_int(int *ptr, int v) { return atomic_fetch_add((_Atomic(uint32_t) *) ptr, v) + v; } static void *tjs__sab_alloc(void *opaque, size_t size) { TJSSABHeader *sab = tjs__malloc(sizeof(*sab) + size); if (!sab) { return NULL; } sab->ref_count = 1; return sab->buf; } void tjs__sab_free(void *opaque, void *ptr) { TJSSABHeader *sab = (TJSSABHeader *) ((uint8_t *) ptr - sizeof(TJSSABHeader)); int ref_count = atomic_add_int(&sab->ref_count, -1); assert(ref_count >= 0); if (ref_count == 0) { tjs__free(sab); } } void tjs__sab_dup(void *opaque, void *ptr) { TJSSABHeader *sab = (TJSSABHeader *) ((uint8_t *) ptr - sizeof(TJSSABHeader)); atomic_add_int(&sab->ref_count, 1); } static const JSSharedArrayBufferFunctions tjs_sf = { .sab_alloc = tjs__sab_alloc, .sab_dup = tjs__sab_dup, .sab_free = tjs__sab_free, .sab_opaque = NULL, }; /* core */ extern const uint8_t tjs__core[]; extern const uint32_t tjs__core_size; extern const uint8_t tjs__polyfills[]; extern const uint32_t tjs__polyfills_size; extern const uint8_t tjs__run_main[]; extern const uint32_t tjs__run_main_size; static int tjs__argc = 0; static char **tjs__argv = NULL; static void tjs__bootstrap_core(JSContext *ctx, JSValue ns) { tjs__mod_dns_init(ctx, ns); tjs__mod_engine_init(ctx, ns); tjs__mod_error_init(ctx, ns); tjs__mod_ffi_init(ctx, ns); tjs__mod_fs_init(ctx, ns); tjs__mod_fswatch_init(ctx, ns); tjs__mod_os_init(ctx, ns); tjs__mod_process_init(ctx, ns); tjs__mod_signals_init(ctx, ns); tjs__mod_sqlite3_init(ctx, ns); tjs__mod_streams_init(ctx, ns); tjs__mod_sys_init(ctx, ns); tjs__mod_timers_init(ctx, ns); tjs__mod_udp_init(ctx, ns); tjs__mod_wasm_init(ctx, ns); tjs__mod_worker_init(ctx, ns); tjs__mod_ws_init(ctx, ns); tjs__mod_xhr_init(ctx, ns); #ifndef _WIN32 tjs__mod_posix_socket_init(ctx, ns); #endif } JSValue tjs__get_args(JSContext *ctx) { JSValue args = JS_NewArray(ctx); for (int i = 0; i < tjs__argc; i++) { JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, tjs__argv[i])); } return args; } static JSValue tjs__dispatch_event(JSContext *ctx, JSValue *event) { TJSRuntime *qrt = TJS_GetRuntime(ctx); CHECK_NOT_NULL(qrt); if (qrt->freeing) { return JS_UNDEFINED; } JSValue global_obj = JS_GetGlobalObject(ctx); JSValue ret = JS_Call(ctx, qrt->builtins.dispatch_event_func, global_obj, 1, event); JS_FreeValue(ctx, global_obj); return ret; } static void tjs__promise_rejection_tracker(JSContext *ctx, JSValue promise, JSValue reason, bool is_handled, void *opaque) { TJSRuntime *qrt = TJS_GetRuntime(ctx); CHECK_NOT_NULL(qrt); if (qrt->freeing) { return; } if (!is_handled) { JSValue event_name = JS_NewString(ctx, "unhandledrejection"); JSValue args[3]; args[0] = event_name; args[1] = promise; args[2] = reason; JSValue event = JS_CallConstructor(ctx, qrt->builtins.promise_event_ctor, countof(args), args); CHECK_EQ(JS_IsException(event), 0); JSValue ret = tjs__dispatch_event(ctx, &event); JS_FreeValue(ctx, event); JS_FreeValue(ctx, event_name); if (JS_IsException(ret)) { tjs_dump_error(ctx); goto fail; } else { if (JS_ToBool(ctx, ret)) { // The event wasn't cancelled, maybe abort. fail:; TJSRuntime *qrt = TJS_GetRuntime(ctx); CHECK_NOT_NULL(qrt); JS_Throw(qrt->ctx, JS_DupValue(qrt->ctx, reason)); TJS_Stop(qrt); } } JS_FreeValue(ctx, ret); } } static void uv__stop(uv_async_t *handle) { TJSRuntime *qrt = handle->data; CHECK_NOT_NULL(qrt); uv_stop(&qrt->loop); } void TJS_DefaultOptions(TJSRunOptions *options) { static TJSRunOptions default_options = { .mem_limit = 0, .stack_size = TJS__DEFAULT_STACK_SIZE }; memcpy(options, &default_options, sizeof(*options)); } TJSRuntime *TJS_NewRuntime(void) { TJSRunOptions options; TJS_DefaultOptions(&options); return TJS_NewRuntimeInternal(false, &options); } TJSRuntime *TJS_NewRuntimeOptions(TJSRunOptions *options) { return TJS_NewRuntimeInternal(false, options); } TJSRuntime *TJS_NewRuntimeWorker(void) { TJSRunOptions options; TJS_DefaultOptions(&options); return TJS_NewRuntimeInternal(true, &options); } TJSRuntime *TJS_NewRuntimeInternal(bool is_worker, TJSRunOptions *options) { JSRuntime *rt = NULL; JSContext *ctx = NULL; TJSRuntime *qrt = tjs__mallocz(sizeof(*qrt)); memcpy(&qrt->options, options, sizeof(*options)); rt = JS_NewRuntime2(&tjs_mf, NULL); CHECK_NOT_NULL(rt); qrt->rt = rt; ctx = JS_NewContext(rt); CHECK_NOT_NULL(ctx); qrt->ctx = ctx; JS_SetRuntimeOpaque(rt, qrt); JS_SetContextOpaque(ctx, qrt); /* Set memory limit */ JS_SetMemoryLimit(rt, options->mem_limit); /* Set stack size */ JS_SetMaxStackSize(rt, options->stack_size); /* SharedArrayBuffer functions */ JS_SetSharedArrayBufferFunctions(rt, &tjs_sf); /* Worker support */ qrt->is_worker = is_worker; JS_SetCanBlock(rt, is_worker); CHECK_EQ(uv_loop_init(&qrt->loop), 0); /* handle which runs the job queue */ CHECK_EQ(uv_prepare_init(&qrt->loop, &qrt->jobs.prepare), 0); qrt->jobs.prepare.data = qrt; /* handle to prevent the loop from blocking for i/o when there are pending jobs. */ CHECK_EQ(uv_idle_init(&qrt->loop, &qrt->jobs.idle), 0); qrt->jobs.idle.data = qrt; /* handle which runs the job queue */ CHECK_EQ(uv_check_init(&qrt->loop, &qrt->jobs.check), 0); qrt->jobs.check.data = qrt; /* handle for stopping this runtime (also works from another thread) */ CHECK_EQ(uv_async_init(&qrt->loop, &qrt->stop, uv__stop), 0); qrt->stop.data = qrt; /* loader for ES modules */ JS_SetModuleLoaderFunc(rt, tjs_module_normalizer, tjs_module_loader, qrt); /* unhandled promise rejection tracker */ JS_SetHostPromiseRejectionTracker(rt, tjs__promise_rejection_tracker, NULL); /* start bootstrap */ JSValue global_obj = JS_GetGlobalObject(ctx); JSValue core_sym = JS_NewSymbol(ctx, "tjs.internal.core", true); JSAtom core_atom = JS_ValueToAtom(ctx, core_sym); JSValue core = JS_NewObjectProto(ctx, JS_NULL); CHECK_EQ(JS_DefinePropertyValue(ctx, global_obj, core_atom, core, JS_PROP_C_W_E), true); CHECK_EQ(JS_DefinePropertyValueStr(ctx, core, "isWorker", JS_NewBool(ctx, is_worker), JS_PROP_C_W_E), true); tjs__bootstrap_core(ctx, core); CHECK_EQ(tjs__eval_bytecode(ctx, tjs__polyfills, tjs__polyfills_size, true), 0); CHECK_EQ(tjs__eval_bytecode(ctx, tjs__core, tjs__core_size, true), 0); /* Load some builtin references for easy access */ qrt->builtins.dispatch_event_func = JS_GetPropertyStr(ctx, global_obj, "dispatchEvent"); CHECK_EQ(JS_IsUndefined(qrt->builtins.dispatch_event_func), 0); qrt->builtins.promise_event_ctor = JS_GetPropertyStr(qrt->ctx, global_obj, "PromiseRejectionEvent"); CHECK_EQ(JS_IsUndefined(qrt->builtins.promise_event_ctor), 0); /* end bootstrap */ JS_FreeAtom(ctx, core_atom); JS_FreeValue(ctx, core_sym); JS_FreeValue(ctx, global_obj); /* WASM */ qrt->wasm_ctx.env = m3_NewEnvironment(); /* Timers */ qrt->timers.timers = NULL; qrt->timers.next_timer = 1; return qrt; } void TJS_FreeRuntime(TJSRuntime *qrt) { qrt->freeing = true; /* Close all core loop handles. */ uv_close((uv_handle_t *) &qrt->jobs.prepare, NULL); uv_close((uv_handle_t *) &qrt->jobs.idle, NULL); uv_close((uv_handle_t *) &qrt->jobs.check, NULL); uv_close((uv_handle_t *) &qrt->stop, NULL); if (qrt->curl_ctx.curlm_h) { uv_close((uv_handle_t *) &qrt->curl_ctx.timer, NULL); } /* Destroy all timers */ tjs__destroy_timers(qrt); /* Destroy the JS engine. */ JS_FreeValue(qrt->ctx, qrt->builtins.dispatch_event_func); qrt->builtins.dispatch_event_func = JS_UNDEFINED; JS_FreeValue(qrt->ctx, qrt->builtins.promise_event_ctor); qrt->builtins.promise_event_ctor = JS_UNDEFINED; JS_FreeContext(qrt->ctx); JS_FreeRuntime(qrt->rt); /* Destroy CURLM handle. */ if (qrt->curl_ctx.curlm_h) { curl_multi_cleanup(qrt->curl_ctx.curlm_h); qrt->curl_ctx.curlm_h = NULL; } /* Destroy WASM runtime. */ m3_FreeEnvironment(qrt->wasm_ctx.env); qrt->wasm_ctx.env = NULL; /* Cleanup loop. All handles should be closed. */ int closed = 0; for (int i = 0; i < 5; i++) { if (uv_loop_close(&qrt->loop) == 0) { closed = 1; break; } uv_run(&qrt->loop, UV_RUN_NOWAIT); } #ifdef DEBUG if (!closed) { uv_print_all_handles(&qrt->loop, stderr); } CHECK_EQ(closed, 1); #else (void) closed; #endif tjs__free(qrt); } void TJS_Initialize(int argc, char **argv) { curl_global_init(CURL_GLOBAL_ALL); CHECK_EQ(0, uv_replace_allocator(tjs__malloc, tjs__realloc, tjs__calloc, tjs__free)); tjs__argc = argc; tjs__argv = uv_setup_args(argc, argv); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif } JSContext *TJS_GetJSContext(TJSRuntime *qrt) { return qrt->ctx; } TJSRuntime *TJS_GetRuntime(JSContext *ctx) { return JS_GetContextOpaque(ctx); } static void uv__idle_cb(uv_idle_t *handle) { // Noop } static void uv__maybe_idle(TJSRuntime *qrt) { if (JS_IsJobPending(qrt->rt)) { CHECK_EQ(uv_idle_start(&qrt->jobs.idle, uv__idle_cb), 0); } else { CHECK_EQ(uv_idle_stop(&qrt->jobs.idle), 0); } } static void uv__prepare_cb(uv_prepare_t *handle) { TJSRuntime *qrt = handle->data; CHECK_NOT_NULL(qrt); uv__maybe_idle(qrt); } void tjs__execute_jobs(JSContext *ctx) { JSContext *ctx1; int err; /* execute the pending jobs */ for (;;) { err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); if (err <= 0) { if (err < 0) { TJSRuntime *qrt = TJS_GetRuntime(ctx); CHECK_NOT_NULL(qrt); TJS_Stop(qrt); } break; } } } static void uv__check_cb(uv_check_t *handle) { TJSRuntime *qrt = handle->data; CHECK_NOT_NULL(qrt); tjs__execute_jobs(qrt->ctx); uv__maybe_idle(qrt); } /* main loop which calls the user JS callbacks */ int TJS_Run(TJSRuntime *qrt) { int ret = 0; CHECK_EQ(uv_prepare_start(&qrt->jobs.prepare, uv__prepare_cb), 0); uv_unref((uv_handle_t *) &qrt->jobs.prepare); CHECK_EQ(uv_check_start(&qrt->jobs.check, uv__check_cb), 0); uv_unref((uv_handle_t *) &qrt->jobs.check); /* Use the async handle to keep the worker alive even when there is nothing to do. */ if (!qrt->is_worker) { uv_unref((uv_handle_t *) &qrt->stop); /* If we are running the main interpreter, run the entrypoint. */ ret = tjs__eval_bytecode(qrt->ctx, tjs__run_main, tjs__run_main_size, true); } if (ret != 0) { return ret; } int r; do { uv__maybe_idle(qrt); r = uv_run(&qrt->loop, UV_RUN_DEFAULT); } while (r == 0 && JS_IsJobPending(qrt->rt)); if (JS_HasException(qrt->ctx)) { tjs_dump_error(qrt->ctx); ret = 1; } return ret; } void TJS_Stop(TJSRuntime *qrt) { CHECK_NOT_NULL(qrt); uv_async_send(&qrt->stop); } uv_loop_t *TJS_GetLoop(TJSRuntime *qrt) { return &qrt->loop; } int tjs__load_file(JSContext *ctx, DynBuf *dbuf, const char *filename) { uv_fs_t req; uv_file fd; int r; r = uv_fs_open(NULL, &req, filename, O_RDONLY, 0, NULL); uv_fs_req_cleanup(&req); if (r < 0) { return r; } fd = r; char buf[64 * 1024]; uv_buf_t b = uv_buf_init(buf, sizeof(buf)); size_t offset = 0; do { r = uv_fs_read(NULL, &req, fd, &b, 1, offset, NULL); uv_fs_req_cleanup(&req); if (r <= 0) { break; } offset += r; r = dbuf_put(dbuf, (const uint8_t *) b.base, r); if (r != 0) { break; } } while (1); uv_fs_close(NULL, &req, fd, NULL); uv_fs_req_cleanup(&req); return r; } JSValue TJS_EvalModuleContent(JSContext *ctx, const char *specifier, bool is_main, bool use_real_path, const char *content, size_t len) { /* Compile then run to be able to set import.meta */ JSValue ret = JS_Eval(ctx, content, len, specifier, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); if (!JS_IsException(ret)) { js_module_set_import_meta(ctx, ret, use_real_path, is_main); ret = JS_EvalFunction(ctx, ret); } /* Emit window 'load' event. */ if (!JS_IsException(ret) && is_main) { static char emit_window_load[] = "window.dispatchEvent(new Event('load'));"; JSValue ret1 = JS_Eval(ctx, emit_window_load, strlen(emit_window_load), "<global>", JS_EVAL_TYPE_GLOBAL); if (JS_IsException(ret1)) { tjs_dump_error(ctx); } } return ret; } JSValue TJS_EvalScript(JSContext *ctx, const char *filename) { DynBuf dbuf; size_t dbuf_size; int r; JSValue ret; tjs_dbuf_init(ctx, &dbuf); r = tjs__load_file(ctx, &dbuf, filename); if (r != 0) { dbuf_free(&dbuf); JS_ThrowReferenceError(ctx, "could not load '%s' - %s: %s", filename, uv_err_name(r), uv_strerror(r)); return JS_EXCEPTION; } dbuf_size = dbuf.size; /* Add null termination, required by JS_Eval. */ dbuf_putc(&dbuf, '\0'); ret = JS_Eval(ctx, (char *) dbuf.buf, dbuf_size - 1, filename, JS_EVAL_TYPE_GLOBAL); dbuf_free(&dbuf); return ret; } JSValue TJS_EvalModule(JSContext *ctx, const char *filename, bool is_main) { DynBuf dbuf; size_t dbuf_size; int r; JSValue ret; tjs_dbuf_init(ctx, &dbuf); r = tjs__load_file(ctx, &dbuf, filename); if (r != 0) { dbuf_free(&dbuf); JS_ThrowReferenceError(ctx, "could not load '%s' - %s: %s", filename, uv_err_name(r), uv_strerror(r)); return JS_EXCEPTION; } dbuf_size = dbuf.size; /* Add null termination, required by JS_Eval. */ dbuf_putc(&dbuf, '\0'); ret = TJS_EvalModuleContent(ctx, filename, is_main, true, (char *) dbuf.buf, dbuf_size - 1); dbuf_free(&dbuf); return ret; }
更改后文本
打开文件
/* * txiki.js * * Copyright (c) 2019-present Saúl Ibarra Corretgé <s@saghul.net> * * 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. */ #include "mem.h" #include "private.h" #include "tjs.h" #include <signal.h> #include <stdatomic.h> #include <stdio.h> #include <string.h> #define TJS__DEFAULT_STACK_SIZE 1024 * 1024 // 1 MB /* JS malloc functions */ static void *tjs__mf_calloc(void *opaque, size_t count, size_t size) { (void) opaque; return tjs__calloc(count, size); } static void *tjs__mf_malloc(void *opaque, size_t size) { (void) opaque; return tjs__malloc(size); } static void tjs__mf_free(void *opaque, void *ptr) { (void) opaque; tjs__free(ptr); } static void *tjs__mf_realloc(void *opaque, void *ptr, size_t size) { (void) opaque; return tjs__realloc(ptr, size); } static const JSMallocFunctions tjs_mf = { .js_calloc = tjs__mf_calloc, .js_malloc = tjs__mf_malloc, .js_free = tjs__mf_free, .js_realloc = tjs__mf_realloc, .js_malloc_usable_size = tjs__malloc_usable_size, }; /* SharedArrayBuffer functions */ typedef struct { int ref_count; uint8_t buf[0]; } TJSSABHeader; static int atomic_add_int(int *ptr, int v) { return atomic_fetch_add((_Atomic(uint32_t) *) ptr, v) + v; } static void *tjs__sab_alloc(void *opaque, size_t size) { TJSSABHeader *sab = tjs__malloc(sizeof(*sab) + size); if (!sab) { return NULL; } sab->ref_count = 1; return sab->buf; } void tjs__sab_free(void *opaque, void *ptr) { TJSSABHeader *sab = (TJSSABHeader *) ((uint8_t *) ptr - sizeof(TJSSABHeader)); int ref_count = atomic_add_int(&sab->ref_count, -1); assert(ref_count >= 0); if (ref_count == 0) { tjs__free(sab); } } void tjs__sab_dup(void *opaque, void *ptr) { TJSSABHeader *sab = (TJSSABHeader *) ((uint8_t *) ptr - sizeof(TJSSABHeader)); atomic_add_int(&sab->ref_count, 1); } static const JSSharedArrayBufferFunctions tjs_sf = { .sab_alloc = tjs__sab_alloc, .sab_dup = tjs__sab_dup, .sab_free = tjs__sab_free, .sab_opaque = NULL, }; /* core */ extern const uint8_t tjs__core[]; extern const uint32_t tjs__core_size; extern const uint8_t tjs__polyfills[]; extern const uint32_t tjs__polyfills_size; extern const uint8_t tjs__run_main[]; extern const uint32_t tjs__run_main_size; static int tjs__argc = 0; static char **tjs__argv = NULL; static void tjs__bootstrap_core(JSContext *ctx, JSValue ns) { tjs__mod_dns_init(ctx, ns); tjs__mod_engine_init(ctx, ns); tjs__mod_error_init(ctx, ns); // tjs__mod_ffi_init(ctx, ns); tjs__mod_fs_init(ctx, ns); tjs__mod_fswatch_init(ctx, ns); tjs__mod_os_init(ctx, ns); tjs__mod_process_init(ctx, ns); tjs__mod_signals_init(ctx, ns); tjs__mod_sqlite3_init(ctx, ns); tjs__mod_streams_init(ctx, ns); tjs__mod_sys_init(ctx, ns); tjs__mod_timers_init(ctx, ns); tjs__mod_udp_init(ctx, ns); tjs__mod_wasm_init(ctx, ns); tjs__mod_worker_init(ctx, ns); tjs__mod_ws_init(ctx, ns); tjs__mod_xhr_init(ctx, ns); #ifndef _WIN32 tjs__mod_posix_socket_init(ctx, ns); #endif } JSValue tjs__get_args(JSContext *ctx) { JSValue args = JS_NewArray(ctx); for (int i = 0; i < tjs__argc; i++) { JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, tjs__argv[i])); } return args; } static JSValue tjs__dispatch_event(JSContext *ctx, JSValue *event) { TJSRuntime *qrt = TJS_GetRuntime(ctx); CHECK_NOT_NULL(qrt); if (qrt->freeing) { return JS_UNDEFINED; } JSValue global_obj = JS_GetGlobalObject(ctx); JSValue ret = JS_Call(ctx, qrt->builtins.dispatch_event_func, global_obj, 1, event); JS_FreeValue(ctx, global_obj); return ret; } static void tjs__promise_rejection_tracker(JSContext *ctx, JSValue promise, JSValue reason, bool is_handled, void *opaque) { TJSRuntime *qrt = TJS_GetRuntime(ctx); CHECK_NOT_NULL(qrt); if (qrt->freeing) { return; } if (!is_handled) { JSValue event_name = JS_NewString(ctx, "unhandledrejection"); JSValue args[3]; args[0] = event_name; args[1] = promise; args[2] = reason; JSValue event = JS_CallConstructor(ctx, qrt->builtins.promise_event_ctor, countof(args), args); CHECK_EQ(JS_IsException(event), 0); JSValue ret = tjs__dispatch_event(ctx, &event); JS_FreeValue(ctx, event); JS_FreeValue(ctx, event_name); if (JS_IsException(ret)) { tjs_dump_error(ctx); goto fail; } else { if (JS_ToBool(ctx, ret)) { // The event wasn't cancelled, maybe abort. fail:; TJSRuntime *qrt = TJS_GetRuntime(ctx); CHECK_NOT_NULL(qrt); JS_Throw(qrt->ctx, JS_DupValue(qrt->ctx, reason)); TJS_Stop(qrt); } } JS_FreeValue(ctx, ret); } } static void uv__stop(uv_async_t *handle) { TJSRuntime *qrt = handle->data; CHECK_NOT_NULL(qrt); uv_stop(&qrt->loop); } void TJS_DefaultOptions(TJSRunOptions *options) { static TJSRunOptions default_options = { .mem_limit = 0, .stack_size = TJS__DEFAULT_STACK_SIZE }; memcpy(options, &default_options, sizeof(*options)); } TJSRuntime *TJS_NewRuntime(void) { TJSRunOptions options; TJS_DefaultOptions(&options); return TJS_NewRuntimeInternal(false, &options); } TJSRuntime *TJS_NewRuntimeOptions(TJSRunOptions *options) { return TJS_NewRuntimeInternal(false, options); } TJSRuntime *TJS_NewRuntimeWorker(void) { TJSRunOptions options; TJS_DefaultOptions(&options); return TJS_NewRuntimeInternal(true, &options); } TJSRuntime *TJS_NewRuntimeInternal(bool is_worker, TJSRunOptions *options) { JSRuntime *rt = NULL; JSContext *ctx = NULL; TJSRuntime *qrt = tjs__mallocz(sizeof(*qrt)); memcpy(&qrt->options, options, sizeof(*options)); rt = JS_NewRuntime2(&tjs_mf, NULL); CHECK_NOT_NULL(rt); qrt->rt = rt; ctx = JS_NewContext(rt); CHECK_NOT_NULL(ctx); qrt->ctx = ctx; JS_SetRuntimeOpaque(rt, qrt); JS_SetContextOpaque(ctx, qrt); /* Set memory limit */ JS_SetMemoryLimit(rt, options->mem_limit); /* Set stack size */ JS_SetMaxStackSize(rt, options->stack_size); /* SharedArrayBuffer functions */ JS_SetSharedArrayBufferFunctions(rt, &tjs_sf); /* Worker support */ qrt->is_worker = is_worker; JS_SetCanBlock(rt, is_worker); CHECK_EQ(uv_loop_init(&qrt->loop), 0); /* handle which runs the job queue */ CHECK_EQ(uv_prepare_init(&qrt->loop, &qrt->jobs.prepare), 0); qrt->jobs.prepare.data = qrt; /* handle to prevent the loop from blocking for i/o when there are pending jobs. */ CHECK_EQ(uv_idle_init(&qrt->loop, &qrt->jobs.idle), 0); qrt->jobs.idle.data = qrt; /* handle which runs the job queue */ CHECK_EQ(uv_check_init(&qrt->loop, &qrt->jobs.check), 0); qrt->jobs.check.data = qrt; /* handle for stopping this runtime (also works from another thread) */ CHECK_EQ(uv_async_init(&qrt->loop, &qrt->stop, uv__stop), 0); qrt->stop.data = qrt; /* loader for ES modules */ JS_SetModuleLoaderFunc(rt, tjs_module_normalizer, tjs_module_loader, qrt); /* unhandled promise rejection tracker */ JS_SetHostPromiseRejectionTracker(rt, tjs__promise_rejection_tracker, NULL); /* start bootstrap */ JSValue global_obj = JS_GetGlobalObject(ctx); JSValue core_sym = JS_NewSymbol(ctx, "tjs.internal.core", true); JSAtom core_atom = JS_ValueToAtom(ctx, core_sym); JSValue core = JS_NewObjectProto(ctx, JS_NULL); CHECK_EQ(JS_DefinePropertyValue(ctx, global_obj, core_atom, core, JS_PROP_C_W_E), true); CHECK_EQ(JS_DefinePropertyValueStr(ctx, core, "isWorker", JS_NewBool(ctx, is_worker), JS_PROP_C_W_E), true); tjs__bootstrap_core(ctx, core); CHECK_EQ(tjs__eval_bytecode(ctx, tjs__polyfills, tjs__polyfills_size, true), 0); CHECK_EQ(tjs__eval_bytecode(ctx, tjs__core, tjs__core_size, true), 0); /* Load some builtin references for easy access */ qrt->builtins.dispatch_event_func = JS_GetPropertyStr(ctx, global_obj, "dispatchEvent"); CHECK_EQ(JS_IsUndefined(qrt->builtins.dispatch_event_func), 0); qrt->builtins.promise_event_ctor = JS_GetPropertyStr(qrt->ctx, global_obj, "PromiseRejectionEvent"); CHECK_EQ(JS_IsUndefined(qrt->builtins.promise_event_ctor), 0); /* end bootstrap */ JS_FreeAtom(ctx, core_atom); JS_FreeValue(ctx, core_sym); JS_FreeValue(ctx, global_obj); /* WASM */ qrt->wasm_ctx.env = m3_NewEnvironment(); /* Timers */ qrt->timers.timers = NULL; qrt->timers.next_timer = 1; return qrt; } void TJS_FreeRuntime(TJSRuntime *qrt) { qrt->freeing = true; /* Close all core loop handles. */ uv_close((uv_handle_t *) &qrt->jobs.prepare, NULL); uv_close((uv_handle_t *) &qrt->jobs.idle, NULL); uv_close((uv_handle_t *) &qrt->jobs.check, NULL); uv_close((uv_handle_t *) &qrt->stop, NULL); if (qrt->curl_ctx.curlm_h) { uv_close((uv_handle_t *) &qrt->curl_ctx.timer, NULL); } /* Destroy all timers */ tjs__destroy_timers(qrt); /* Destroy the JS engine. */ JS_FreeValue(qrt->ctx, qrt->builtins.dispatch_event_func); qrt->builtins.dispatch_event_func = JS_UNDEFINED; JS_FreeValue(qrt->ctx, qrt->builtins.promise_event_ctor); qrt->builtins.promise_event_ctor = JS_UNDEFINED; JS_FreeContext(qrt->ctx); JS_FreeRuntime(qrt->rt); /* Destroy CURLM handle. */ if (qrt->curl_ctx.curlm_h) { curl_multi_cleanup(qrt->curl_ctx.curlm_h); qrt->curl_ctx.curlm_h = NULL; } /* Destroy WASM runtime. */ m3_FreeEnvironment(qrt->wasm_ctx.env); qrt->wasm_ctx.env = NULL; /* Cleanup loop. All handles should be closed. */ int closed = 0; for (int i = 0; i < 5; i++) { if (uv_loop_close(&qrt->loop) == 0) { closed = 1; break; } uv_run(&qrt->loop, UV_RUN_NOWAIT); } #ifdef DEBUG if (!closed) { uv_print_all_handles(&qrt->loop, stderr); } CHECK_EQ(closed, 1); #else (void) closed; #endif tjs__free(qrt); } void TJS_Initialize(int argc, char **argv) { curl_global_init(CURL_GLOBAL_ALL); CHECK_EQ(0, uv_replace_allocator(tjs__malloc, tjs__realloc, tjs__calloc, tjs__free)); tjs__argc = argc; tjs__argv = uv_setup_args(argc, argv); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif } JSContext *TJS_GetJSContext(TJSRuntime *qrt) { return qrt->ctx; } TJSRuntime *TJS_GetRuntime(JSContext *ctx) { return JS_GetContextOpaque(ctx); } static void uv__idle_cb(uv_idle_t *handle) { // Noop } static void uv__maybe_idle(TJSRuntime *qrt) { if (JS_IsJobPending(qrt->rt)) { CHECK_EQ(uv_idle_start(&qrt->jobs.idle, uv__idle_cb), 0); } else { CHECK_EQ(uv_idle_stop(&qrt->jobs.idle), 0); } } static void uv__prepare_cb(uv_prepare_t *handle) { TJSRuntime *qrt = handle->data; CHECK_NOT_NULL(qrt); uv__maybe_idle(qrt); } void tjs__execute_jobs(JSContext *ctx) { JSContext *ctx1; int err; /* execute the pending jobs */ for (;;) { err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); if (err <= 0) { if (err < 0) { TJSRuntime *qrt = TJS_GetRuntime(ctx); CHECK_NOT_NULL(qrt); TJS_Stop(qrt); } break; } } } static void uv__check_cb(uv_check_t *handle) { TJSRuntime *qrt = handle->data; CHECK_NOT_NULL(qrt); tjs__execute_jobs(qrt->ctx); uv__maybe_idle(qrt); } /* main loop which calls the user JS callbacks */ int TJS_Run(TJSRuntime *qrt) { int ret = 0; CHECK_EQ(uv_prepare_start(&qrt->jobs.prepare, uv__prepare_cb), 0); uv_unref((uv_handle_t *) &qrt->jobs.prepare); CHECK_EQ(uv_check_start(&qrt->jobs.check, uv__check_cb), 0); uv_unref((uv_handle_t *) &qrt->jobs.check); /* Use the async handle to keep the worker alive even when there is nothing to do. */ if (!qrt->is_worker) { uv_unref((uv_handle_t *) &qrt->stop); /* If we are running the main interpreter, run the entrypoint. */ ret = tjs__eval_bytecode(qrt->ctx, tjs__run_main, tjs__run_main_size, true); } if (ret != 0) { return ret; } int r; do { uv__maybe_idle(qrt); r = uv_run(&qrt->loop, UV_RUN_DEFAULT); } while (r == 0 && JS_IsJobPending(qrt->rt)); if (JS_HasException(qrt->ctx)) { tjs_dump_error(qrt->ctx); ret = 1; } return ret; } void TJS_Stop(TJSRuntime *qrt) { CHECK_NOT_NULL(qrt); uv_async_send(&qrt->stop); } uv_loop_t *TJS_GetLoop(TJSRuntime *qrt) { return &qrt->loop; } int tjs__load_file(JSContext *ctx, DynBuf *dbuf, const char *filename) { uv_fs_t req; uv_file fd; int r; r = uv_fs_open(NULL, &req, filename, O_RDONLY, 0, NULL); uv_fs_req_cleanup(&req); if (r < 0) { return r; } fd = r; char buf[64 * 1024]; uv_buf_t b = uv_buf_init(buf, sizeof(buf)); size_t offset = 0; do { r = uv_fs_read(NULL, &req, fd, &b, 1, offset, NULL); uv_fs_req_cleanup(&req); if (r <= 0) { break; } offset += r; r = dbuf_put(dbuf, (const uint8_t *) b.base, r); if (r != 0) { break; } } while (1); uv_fs_close(NULL, &req, fd, NULL); uv_fs_req_cleanup(&req); return r; } JSValue TJS_EvalModuleContent(JSContext *ctx, const char *specifier, bool is_main, bool use_real_path, const char *content, size_t len) { /* Compile then run to be able to set import.meta */ JSValue ret = JS_Eval(ctx, content, len, specifier, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); if (!JS_IsException(ret)) { js_module_set_import_meta(ctx, ret, use_real_path, is_main); ret = JS_EvalFunction(ctx, ret); } /* Emit window 'load' event. */ if (!JS_IsException(ret) && is_main) { static char emit_window_load[] = "window.dispatchEvent(new Event('load'));"; JSValue ret1 = JS_Eval(ctx, emit_window_load, strlen(emit_window_load), "<global>", JS_EVAL_TYPE_GLOBAL); if (JS_IsException(ret1)) { tjs_dump_error(ctx); } } return ret; } JSValue TJS_EvalScript(JSContext *ctx, const char *filename) { DynBuf dbuf; size_t dbuf_size; int r; JSValue ret; tjs_dbuf_init(ctx, &dbuf); r = tjs__load_file(ctx, &dbuf, filename); if (r != 0) { dbuf_free(&dbuf); JS_ThrowReferenceError(ctx, "could not load '%s' - %s: %s", filename, uv_err_name(r), uv_strerror(r)); return JS_EXCEPTION; } dbuf_size = dbuf.size; /* Add null termination, required by JS_Eval. */ dbuf_putc(&dbuf, '\0'); ret = JS_Eval(ctx, (char *) dbuf.buf, dbuf_size - 1, filename, JS_EVAL_TYPE_GLOBAL); dbuf_free(&dbuf); return ret; } JSValue TJS_EvalModule(JSContext *ctx, const char *filename, bool is_main) { DynBuf dbuf; size_t dbuf_size; int r; JSValue ret; tjs_dbuf_init(ctx, &dbuf); r = tjs__load_file(ctx, &dbuf, filename); if (r != 0) { dbuf_free(&dbuf); JS_ThrowReferenceError(ctx, "could not load '%s' - %s: %s", filename, uv_err_name(r), uv_strerror(r)); return JS_EXCEPTION; } dbuf_size = dbuf.size; /* Add null termination, required by JS_Eval. */ dbuf_putc(&dbuf, '\0'); ret = TJS_EvalModuleContent(ctx, filename, is_main, true, (char *) dbuf.buf, dbuf_size - 1); dbuf_free(&dbuf); return ret; }
查找差异