summaryrefslogtreecommitdiff
path: root/content/handlers/javascript/duktape
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2020-03-21 20:01:38 +0000
committerDaniel Silverstone <dsilvers@digital-scurf.org>2020-03-21 20:01:38 +0000
commit3c4652c1c3a2231b2bb1e2bf43e6c16fc54d10bf (patch)
tree2b050331257c08e56880cdaeafa60eea05d97193 /content/handlers/javascript/duktape
parent66a23c25605c9c7eca7b5c9b420a079ce1203faf (diff)
downloadnetsurf-3c4652c1c3a2231b2bb1e2bf43e6c16fc54d10bf.tar.gz
netsurf-3c4652c1c3a2231b2bb1e2bf43e6c16fc54d10bf.tar.bz2
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 <dsilvers@digital-scurf.org>
Diffstat (limited to 'content/handlers/javascript/duktape')
-rw-r--r--content/handlers/javascript/duktape/Window.bnd12
-rw-r--r--content/handlers/javascript/duktape/dukky.c202
-rw-r--r--content/handlers/javascript/duktape/polyfill.js2
3 files changed, 121 insertions, 95 deletions
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.
*/