From 3c4652c1c3a2231b2bb1e2bf43e6c16fc54d10bf Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 21 Mar 2020 20:01:38 +0000 Subject: Dukky: Upgrade dukky binding to support new heap/thread split Update the duktape bindings and dukky interface to support the new JS heap/thread split. Heaps may have multiple active threads though in general there will only be 2 at a time. Signed-off-by: Daniel Silverstone --- content/handlers/javascript/duktape/Window.bnd | 12 +- content/handlers/javascript/duktape/dukky.c | 202 +++++++++++++----------- content/handlers/javascript/duktape/polyfill.js | 2 +- 3 files changed, 121 insertions(+), 95 deletions(-) (limited to 'content/handlers/javascript') diff --git a/content/handlers/javascript/duktape/Window.bnd b/content/handlers/javascript/duktape/Window.bnd index 44a5170d3..b4a467dfb 100644 --- a/content/handlers/javascript/duktape/Window.bnd +++ b/content/handlers/javascript/duktape/Window.bnd @@ -216,8 +216,8 @@ window_remove_callback_by_handle(duk_context *ctx, } RING_ITERATE_END(window->schedule_ring, sched); } -/* This is the dodgy compartment closedown method */ -static duk_ret_t dukky_window_closedown_compartment(duk_context *ctx) +/* This is the dodgy thread closedown method */ +static duk_ret_t dukky_window_closedown_thread(duk_context *ctx) { window_private_t *priv = NULL; @@ -230,7 +230,7 @@ static duk_ret_t dukky_window_closedown_compartment(duk_context *ctx) return 0; } - NSLOG(dukky, DEEPDEBUG, "Closing down compartment"); + NSLOG(dukky, DEEPDEBUG, "Closing down thread"); while (priv->schedule_ring != NULL) { window_remove_callback_by_handle(ctx, priv, @@ -361,10 +361,10 @@ prototype Window() /* ** WebAssembly */ /* As yet, Duktape lacks WA */ #undef EXPOSE - /* Add s3kr1t method to close the compartment */ + /* Add s3kr1t method to close the JS thread (browsing context) */ duk_dup(ctx, 0); - duk_push_string(ctx, MAGIC(closedownCompartment)); - duk_push_c_function(ctx, dukky_window_closedown_compartment, DUK_VARARGS); + duk_push_string(ctx, MAGIC(closedownThread)); + duk_push_c_function(ctx, dukky_window_closedown_thread, DUK_VARARGS); duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE | diff --git a/content/handlers/javascript/duktape/dukky.c b/content/handlers/javascript/duktape/dukky.c index 560a12f56..f47dec895 100644 --- a/content/handlers/javascript/duktape/dukky.c +++ b/content/handlers/javascript/duktape/dukky.c @@ -51,16 +51,26 @@ #define HANDLER_MAGIC MAGIC(HANDLER_MAP) #define EVENT_LISTENER_JS_MAGIC MAGIC(EVENT_LISTENER_JS_MAP) #define GENERICS_MAGIC MAGIC(GENERICS_TABLE) +#define THREAD_MAP MAGIC(THREAD_MAP) /** - * dukky javascript context + * dukky javascript heap */ -struct jscontext { +struct jsheap { duk_context *ctx; /**< duktape base context */ - duk_context *thread; /**< duktape compartment */ + duk_uarridx_t next_thread; /**< monotonic thread counter */ uint64_t exec_start_time; }; +/** + * dukky javascript thread + */ +struct jsthread { + jsheap *heap; /**< The heap this thread belongs to */ + duk_context *ctx; /**< The duktape thread context */ + duk_uarridx_t thread_idx; /**< The thread number */ +}; + static duk_ret_t dukky_populate_object(duk_context *ctx, void *udata) { /* ... obj args protoname nargs */ @@ -556,36 +566,6 @@ static void dukky_free_function(void *udata, void *ptr) free(ptr); } - -#define CTX (ctx->thread) - -/** - * close current compartment - * - * \param ctx javascript context - * \return NSERROR_OK on sucess. - */ -static nserror dukky_closecompartment(jscontext *ctx) -{ - /* ensure there is an active compartment */ - if (ctx->thread == NULL) { - return NSERROR_OK; - } - - /* Closing down the extant compartment */ - NSLOG(dukky, DEEPDEBUG, "Closing down extant compartment..."); - duk_get_global_string(ctx->thread, MAGIC(closedownCompartment)); - dukky_pcall(CTX, 0, true); - NSLOG(dukky, DEEPDEBUG, "Popping the thread off the stack"); - duk_set_top(ctx->ctx, 0); - duk_gc(ctx->ctx, 0); - duk_gc(ctx->ctx, DUK_GC_COMPACT); - - ctx->thread = NULL; - - return NSERROR_OK; -} - /* exported interface documented in js.h */ void js_initialise(void) { @@ -608,12 +588,12 @@ void js_finalise(void) /* exported interface documented in js.h */ nserror -js_newcontext(int timeout, jscontext **jsctx) +js_newheap(int timeout, jsheap **heap) { duk_context *ctx; - jscontext *ret = calloc(1, sizeof(*ret)); - *jsctx = NULL; - NSLOG(dukky, DEBUG, "Creating new duktape javascript context"); + jsheap *ret = calloc(1, sizeof(*ret)); + *heap = NULL; + NSLOG(dukky, DEBUG, "Creating new duktape javascript heap"); if (ret == NULL) return NSERROR_NOMEM; ctx = ret->ctx = duk_create_heap( dukky_alloc_function, @@ -629,36 +609,50 @@ js_newcontext(int timeout, jscontext **jsctx) duk_put_global_string(ctx, PROTO_MAGIC); /* Create prototypes here */ dukky_create_prototypes(ctx); + /* Now create the thread map */ + duk_push_object(ctx); + duk_put_global_string(ctx, THREAD_MAP); - *jsctx = ret; + *heap = ret; return NSERROR_OK; } /* exported interface documented in js.h */ -void js_destroycontext(jscontext *ctx) +void js_destroyheap(jsheap *heap) { NSLOG(dukky, DEBUG, "Destroying duktape javascript context"); - dukky_closecompartment(ctx); - duk_destroy_heap(ctx->ctx); - free(ctx); + duk_destroy_heap(heap->ctx); + free(heap); } +/* Just for here, the CTX is in ret, not thread */ +#define CTX (ret->ctx) /* exported interface documented in js.h */ -jsobject *js_newcompartment(jscontext *ctx, void *win_priv, void *doc_priv) +nserror js_newthread(jsheap *heap, void *win_priv, void *doc_priv, jsthread **thread) { - assert(ctx != NULL); + jsthread *ret; + assert(heap != NULL); + + ret = calloc(1, sizeof (*ret)); + if (ret == NULL) { + NSLOG(dukky, ERROR, "Unable to allocate new JS thread structure"); + return NSERROR_NOMEM; + } + NSLOG(dukky, DEBUG, - "New javascript/duktape compartment, win_priv=%p, doc_priv=%p", + "New javascript/duktape thread, win_priv=%p, doc_priv=%p", win_priv, doc_priv); - /* Pop any active thread off */ - dukky_closecompartment(ctx); - - /* create new compartment thread */ - duk_push_thread(ctx->ctx); - ctx->thread = duk_require_context(ctx->ctx, -1); + /* create new thread */ + duk_get_global_string(heap->ctx, THREAD_MAP); /* ... threads */ + duk_push_thread(heap->ctx); /* ... threads thread */ + ret->heap = heap; + ret->ctx = duk_require_context(heap->ctx, -1); + ret->thread_idx = heap->next_thread++; + duk_put_prop_index(heap->ctx, -2, ret->thread_idx); + duk_pop(heap->ctx); /* ... */ duk_push_int(CTX, 0); duk_push_int(CTX, 1); duk_push_int(CTX, 2); @@ -689,13 +683,15 @@ jsobject *js_newcompartment(jscontext *ctx, void *win_priv, void *doc_priv) if (duk_pcompile_lstring_filename(CTX, DUK_COMPILE_EVAL, (const char *)polyfill_js, polyfill_js_len) != 0) { NSLOG(dukky, CRITICAL, "%s", duk_safe_to_string(CTX, -1)); - NSLOG(dukky, CRITICAL, "Unable to compile polyfill.js, compartment aborted"); - return NULL; + NSLOG(dukky, CRITICAL, "Unable to compile polyfill.js, thread aborted"); + js_destroythread(ret); + return NSERROR_INIT_FAILED; } /* ..., (generics.js) */ if (dukky_pcall(CTX, 0, true) != 0) { - NSLOG(dukky, CRITICAL, "Unable to run polyfill.js, compartment aborted"); - return NULL; + NSLOG(dukky, CRITICAL, "Unable to run polyfill.js, thread aborted"); + js_destroythread(ret); + return NSERROR_INIT_FAILED; } /* ..., result */ duk_pop(CTX); @@ -708,13 +704,15 @@ jsobject *js_newcompartment(jscontext *ctx, void *win_priv, void *doc_priv) if (duk_pcompile_lstring_filename(CTX, DUK_COMPILE_EVAL, (const char *)generics_js, generics_js_len) != 0) { NSLOG(dukky, CRITICAL, "%s", duk_safe_to_string(CTX, -1)); - NSLOG(dukky, CRITICAL, "Unable to compile generics.js, compartment aborted"); - return NULL; + NSLOG(dukky, CRITICAL, "Unable to compile generics.js, thread aborted"); + js_destroythread(ret); + return NSERROR_INIT_FAILED; } /* ..., (generics.js) */ if (dukky_pcall(CTX, 0, true) != 0) { - NSLOG(dukky, CRITICAL, "Unable to run generics.js, compartment aborted"); - return NULL; + NSLOG(dukky, CRITICAL, "Unable to run generics.js, thread aborted"); + js_destroythread(ret); + return NSERROR_INIT_FAILED; } /* ..., result */ duk_pop(CTX); @@ -729,15 +727,44 @@ jsobject *js_newcompartment(jscontext *ctx, void *win_priv, void *doc_priv) duk_pop(CTX); /* ... */ - dukky_log_stack_frame(CTX, "New compartment created"); + dukky_log_stack_frame(CTX, "New thread created"); + NSLOG(dukky, DEBUG, "New thread is %p in heap %p", thread, heap); + *thread = ret; - return (jsobject *)ctx; + return NSERROR_OK; +} + +/* Now switch to the long term CTX behaviour */ +#undef CTX +#define CTX (thread->ctx) + + +/* exported interface documented in js.h */ +void js_destroythread(jsthread *thread) +{ + jsheap *heap = thread->heap; + /* Closing down the extant thread */ + NSLOG(dukky, DEBUG, "Closing down extant thread %p in heap %p", thread, heap); + duk_get_global_string(CTX, MAGIC(closedownThread)); + dukky_pcall(CTX, 0, true); + + /* Now delete the thread from the heap */ + duk_get_global_string(heap->ctx, THREAD_MAP); /* ... threads */ + duk_del_prop_index(heap->ctx, -1, thread->thread_idx); + duk_pop(heap->ctx); /* ... */ + + /* We can now free the thread object */ + free(thread); + + /* Finally give the heap a chance to clean up */ + duk_gc(heap->ctx, 0); + duk_gc(heap->ctx, DUK_GC_COMPACT); } duk_bool_t dukky_check_timeout(void *udata) { #define JS_EXEC_TIMEOUT_MS 10000 /* 10 seconds */ - jscontext *ctx = (jscontext *) udata; + jsheap *heap = (jsheap *) udata; uint64_t now; (void) nsu_getmonotonic_ms(&now); @@ -746,8 +773,8 @@ duk_bool_t dukky_check_timeout(void *udata) * so only test for execution timeout if we've recorded a * start time. */ - return ctx->exec_start_time != 0 && - now > (ctx->exec_start_time + JS_EXEC_TIMEOUT_MS); + return heap->exec_start_time != 0 && + now > (heap->exec_start_time + JS_EXEC_TIMEOUT_MS); } static void dukky_dump_error(duk_context *ctx) @@ -761,14 +788,19 @@ static void dukky_dump_error(duk_context *ctx) /* ..., errobj */ } +static void dukky_reset_start_time(duk_context *ctx) +{ + duk_memory_functions funcs; + jsheap *heap; + duk_get_memory_functions(ctx, &funcs); + heap = funcs.udata; + (void) nsu_getmonotonic_ms(&heap->exec_start_time); +} + duk_int_t dukky_pcall(duk_context *ctx, duk_size_t argc, bool reset_timeout) { if (reset_timeout) { - duk_memory_functions funcs; - jscontext *jsctx; - duk_get_memory_functions(ctx, &funcs); - jsctx = funcs.udata; - (void) nsu_getmonotonic_ms(&jsctx->exec_start_time); + dukky_reset_start_time(ctx); } duk_int_t ret = duk_pcall(ctx, argc); @@ -811,9 +843,9 @@ void dukky_log_stack_frame(duk_context *ctx, const char * reason) /* exported interface documented in js.h */ bool -js_exec(jscontext *ctx, const uint8_t *txt, size_t txtlen, const char *name) +js_exec(jsthread *thread, const uint8_t *txt, size_t txtlen, const char *name) { - assert(ctx); + assert(thread); if (txt == NULL || txtlen == 0) { return false; @@ -823,7 +855,7 @@ js_exec(jscontext *ctx, const uint8_t *txt, size_t txtlen, const char *name) NSLOG(dukky, DEEPDEBUG, "Running %"PRIsizet" bytes from %s", txtlen, name); /* NSLOG(dukky, DEEPDEBUG, "\n%s\n", txt); */ - (void) nsu_getmonotonic_ms(&ctx->exec_start_time); + dukky_reset_start_time(CTX); if (name != NULL) { duk_push_string(CTX, name); } else { @@ -1023,9 +1055,7 @@ bool dukky_get_current_value_of_event_handler(duk_context *ctx, static void dukky_generic_event_handler(dom_event *evt, void *pw) { - duk_memory_functions funcs; duk_context *ctx = (duk_context *)pw; - jscontext *jsctx; dom_string *name; dom_exception exc; dom_event_target *targ; @@ -1033,10 +1063,6 @@ static void dukky_generic_event_handler(dom_event *evt, void *pw) duk_uarridx_t idx; event_listener_flags flags; - /* Retrieve the JS context from the Duktape context */ - duk_get_memory_functions(ctx, &funcs); - jsctx = funcs.udata; - NSLOG(dukky, DEBUG, "Handling an event in duktape interface..."); exc = dom_event_get_type(evt, &name); if (exc != DOM_NO_ERR) { @@ -1084,7 +1110,7 @@ static void dukky_generic_event_handler(dom_event *evt, void *pw) /* ... handler node */ dukky_push_event(ctx, evt); /* ... handler node event */ - (void) nsu_getmonotonic_ms(&jsctx->exec_start_time); + dukky_reset_start_time(ctx); if (duk_pcall_method(ctx, 1) != 0) { /* Failed to run the method */ /* ... err */ @@ -1183,7 +1209,7 @@ handle_extras: /* ... copy handler callback node */ dukky_push_event(ctx, evt); /* ... copy handler callback node event */ - (void) nsu_getmonotonic_ms(&jsctx->exec_start_time); + dukky_reset_start_time(ctx); if (duk_pcall_method(ctx, 1) != 0) { /* Failed to run the method */ /* ... copy handler err */ @@ -1355,9 +1381,9 @@ void dukky_shuffle_array(duk_context *ctx, duk_uarridx_t idx) } -void js_handle_new_element(jscontext *ctx, struct dom_element *node) +void js_handle_new_element(jsthread *thread, struct dom_element *node) { - assert(ctx); + assert(thread); assert(node); dom_namednodemap *map; dom_exception exc; @@ -1431,9 +1457,9 @@ out: dom_namednodemap_unref(map); } -void js_event_cleanup(jscontext *ctx, struct dom_event *evt) +void js_event_cleanup(jsthread *thread, struct dom_event *evt) { - assert(ctx); + assert(thread); /* ... */ duk_get_global_string(CTX, EVENT_MAGIC); /* ... EVENT_MAP */ @@ -1445,7 +1471,7 @@ void js_event_cleanup(jscontext *ctx, struct dom_event *evt) /* ... */ } -bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, struct dom_node *target) +bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc, struct dom_node *target) { dom_exception exc; dom_event *evt; @@ -1523,7 +1549,7 @@ bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, s /* ... handler Window */ dukky_push_event(CTX, evt); /* ... handler Window event */ - (void) nsu_getmonotonic_ms(&ctx->exec_start_time); + dukky_reset_start_time(CTX); if (duk_pcall_method(CTX, 1) != 0) { /* Failed to run the handler */ /* ... err */ @@ -1546,14 +1572,14 @@ bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, s duk_pop_n(CTX, 6); /* ... */ - js_event_cleanup(ctx, evt); + js_event_cleanup(thread, evt); dom_event_unref(evt); return true; } /* ... result */ duk_pop(CTX); /* ... */ - js_event_cleanup(ctx, evt); + js_event_cleanup(thread, evt); dom_event_unref(evt); return true; } diff --git a/content/handlers/javascript/duktape/polyfill.js b/content/handlers/javascript/duktape/polyfill.js index 7fa46111f..5f0215618 100644 --- a/content/handlers/javascript/duktape/polyfill.js +++ b/content/handlers/javascript/duktape/polyfill.js @@ -1,6 +1,6 @@ /* Polyfiller for Duktape for NetSurf * - * This JavaScript will be loaded into compartments before the generics + * This JavaScript will be loaded into heaps before the generics * * We only care for the side-effects of this, be careful. */ -- cgit v1.2.3