From 68bb7471bae8be36e119866984fa00339371cb44 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Thu, 30 Jan 2020 17:15:49 +0000 Subject: [PATCH 1/2] Allow async functions to throw Provide a convenient means to allow asynchronous functions to throw an exception rather than return a value. --- demos/async.html | 23 ++++++++++++++++++++--- interpreter.js | 9 +++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/demos/async.html b/demos/async.html index d166a080..cf1e28a0 100644 --- a/demos/async.html +++ b/demos/async.html @@ -19,8 +19,16 @@ var req = new XMLHttpRequest(); req.open('GET', href, true); req.onreadystatechange = function() { - if (req.readyState == 4 && req.status == 200) { - callback(req.responseText); + if (req.readyState == 4) { + if(req.status == 200) { + callback(req.responseText); + } else { + var error = interpreter.createObject(interpreter.ERROR); + interpreter.setProperty(error, 'message', + req.status + req.statusText, + Interpreter.NONENUMERABLE_DESCRIPTOR); + callback(undefined, error); + } } }; req.send(null); @@ -102,11 +110,20 @@

JS-Interpreter Async Demo

will both do nothing, returning true to indicate that the program still has code to execute.

+

The callback takes one or two arguments. If called with one, the + supplied value is used as the return value of the asynchronous + call. If called with two, the first is ignored and the second is + used as the value of an exception thrown by the call.

+

Click Parse, then either click Step repeatedly, or click Run once. Open your browser's console for errors.


diff --git a/interpreter.js b/interpreter.js index 917f10e1..07b0034d 100644 --- a/interpreter.js +++ b/interpreter.js @@ -3226,8 +3226,13 @@ Interpreter.prototype['stepCallExpression'] = function(stack, state, node) { state.value = func.nativeFunc.apply(state.funcThis_, state.arguments_); } else if (func.asyncFunc) { var thisInterpreter = this; - var callback = function(value) { - state.value = value; + var callback = function(value, exception) { + if (arguments.length === 1) { + state.value = value; + } else { + thisInterpreter.unwind(Interpreter.Completion.THROW, + exception, undefined); + } thisInterpreter.paused_ = false; }; // Force the argument lengths to match, then append the callback. From 22f6493000d2325a2b9986bf498ede80693d667a Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Mon, 2 Mar 2020 15:08:48 -0800 Subject: [PATCH 2/2] Async callback with no args should return Existing usage includes calling the callback with no arguments, intending it to return undefined. Preserve this behaviour by treating zero and one arguments equivalently, and only throwing if it is called with two or more. (This will break any existing usage which calls the callback with more than one argument, but that ought to be fairly uncommon!) --- interpreter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter.js b/interpreter.js index 07b0034d..3115f4da 100644 --- a/interpreter.js +++ b/interpreter.js @@ -3227,7 +3227,7 @@ Interpreter.prototype['stepCallExpression'] = function(stack, state, node) { } else if (func.asyncFunc) { var thisInterpreter = this; var callback = function(value, exception) { - if (arguments.length === 1) { + if (arguments.length <= 1) { state.value = value; } else { thisInterpreter.unwind(Interpreter.Completion.THROW,