From 5ba4c7f1b655732ef783e37b37d99d8afedc232a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Nov 2023 15:51:16 +0100 Subject: [PATCH] Add queueMicrotask Ref: https://github.com/quickjs-ng/quickjs/issues/16 --- Makefile | 1 + quickjs.c | 17 +++++++ tests/test_queue_microtask.js | 84 +++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 tests/test_queue_microtask.js diff --git a/Makefile b/Makefile index ae37d2b8..a837445d 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,7 @@ test: build $(QJS) tests/test_loop.js $(QJS) tests/test_std.js $(QJS) tests/test_worker.js + $(QJS) tests/test_queue_microtask.js test262: build $(RUN262) -m -c test262.conf -a diff --git a/quickjs.c b/quickjs.c index 9e8d7f9a..b2f8f028 100644 --- a/quickjs.c +++ b/quickjs.c @@ -33745,6 +33745,22 @@ static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val, return JS_NewBool(ctx, res); } +static JSValue js_microtask_job(JSContext *ctx, + int argc, JSValueConst *argv) +{ + return JS_Call(ctx, argv[0], ctx->global_obj, 0, NULL); +} + +static JSValue js_global_queueMicrotask(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + if (check_function(ctx, argv[0])) + return JS_EXCEPTION; + if (JS_EnqueueJob(ctx, js_microtask_job, 1, &argv[0])) + return JS_EXCEPTION; + return JS_UNDEFINED; +} + /* Object class */ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val) @@ -45683,6 +45699,7 @@ static const JSCFunctionListEntry js_global_funcs[] = { JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ), JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ), JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ), + JS_CFUNC_DEF("queueMicrotask", 1, js_global_queueMicrotask ), JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ), JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ), diff --git a/tests/test_queue_microtask.js b/tests/test_queue_microtask.js new file mode 100644 index 00000000..106dde43 --- /dev/null +++ b/tests/test_queue_microtask.js @@ -0,0 +1,84 @@ +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) + return; + + if (actual !== null && expected !== null + && typeof actual == 'object' && typeof expected == 'object' + && actual.toString() === expected.toString()) + return; + + throw Error("assertion failed: got |" + actual + "|" + + ", expected |" + expected + "|" + + (message ? " (" + message + ")" : "")); +} + +function assert_throws(expected_error, func) +{ + var err = false; + try { + func(); + } catch(e) { + err = true; + if (!(e instanceof expected_error)) { + throw Error("unexpected exception type"); + } + } + if (!err) { + throw Error("expected exception"); + } +} + +function assert_array_equals(a, b) { + if (!Array.isArray(a) || !Array.isArray(b)) + return assert(false); + + assert(a.length === b.length); + + a.forEach((value, idx) => { + assert(b[idx] === value); + }); +} + +// load more elaborate version of assert if available +try { std.loadScript("test_assert.js"); } catch(e) {} + +function test_types() { + assert_throws(TypeError, () => queueMicrotask(), "no argument"); + assert_throws(TypeError, () => queueMicrotask(undefined), "undefined"); + assert_throws(TypeError, () => queueMicrotask(null), "null"); + assert_throws(TypeError, () => queueMicrotask(0), "0"); + assert_throws(TypeError, () => queueMicrotask({ handleEvent() { } }), "an event handler object"); + assert_throws(TypeError, () => queueMicrotask("window.x = 5;"), "a string"); +} + +function test_async() { + let called = false; + queueMicrotask(() => { + called = true; + }); + assert(!called); +} + +function test_arguments() { + queueMicrotask(function () { // note: intentionally not an arrow function + assert(arguments.length === 0); + }, "x", "y"); +}; + +function test_async_order() { + const happenings = []; + Promise.resolve().then(() => happenings.push("a")); + queueMicrotask(() => happenings.push("b")); + Promise.reject().catch(() => happenings.push("c")); + queueMicrotask(() => { + assert_array_equals(happenings, ["a", "b", "c"]); + }); +} + +test_types(); +test_async(); +test_arguments(); +test_async_order();