Diff
checker
Texte
Texte
Images
Documents
Excel
Dossiers
Legal
Enterprise
Application de bureau
Prix
Se connecter
Télécharger Diffchecker Desktop
Comparer le texte
Trouver la différence entre deux fichiers texte
Outils
Historique
Éditeur live
Masquer les espaces
Cacher identiques
Sans retour à la ligne
Vue
Divisé
Unifié
Niveau de précision
Intelligent
Mot
Caractère
Styles de texte
Modifier l’apparence
Coloration syntaxique
Choisir la syntaxe
Ignorer
Transformer le texte
Aller au premier écart
Modifier l'entrée
Diffchecker Desktop
La façon la plus sécurisée d'utiliser Diffchecker. Obtenez l'application Diffchecker Desktop : vos diffs ne quittent jamais votre ordinateur !
Obtenir Desktop
txiki.js vm.c FFI support removed
Créé
il y a 10 mois
Le diff n'expire jamais
Effacer
Exporter
Partager
Expliquer
0 suppressions
Lignes
Total
Supprimé
Caractères
Total
Supprimé
Pour continuer à utiliser cette fonctionnalité, passez à
Diff
checker
Pro
Voir les prix
607 lignes
Copier tout
2 ajouts
Lignes
Total
Ajouté
Caractères
Total
Ajouté
Pour continuer à utiliser cette fonctionnalité, passez à
Diff
checker
Pro
Voir les prix
608 lignes
Copier tout
/*
/*
* 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);
Copier
Copié
Copier
Copié
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;
}
}
Copier
Copié
Copier
Copié
Différences enregistrées
Texte d'origine
Ouvrir un fichier
/* * 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; }
Texte modifié
Ouvrir un fichier
/* * 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; }
Trouver la différence