From 969913790ce9202295c768bb18307cdab5f81a03 Mon Sep 17 00:00:00 2001 From: thejohncrafter Date: Sun, 14 May 2017 14:40:11 +0200 Subject: [PATCH 1/3] Add native callbacks. --- demos/callback.html | 131 ++++++++++++++++++++++++++++++++++++++++++++ docs.html | 22 ++++++++ interpreter.js | 36 ++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 demos/callback.html diff --git a/demos/callback.html b/demos/callback.html new file mode 100644 index 00000000..f376bf30 --- /dev/null +++ b/demos/callback.html @@ -0,0 +1,131 @@ + + + + + JS-Interpreter Callback Demo + + + + + + +

JS-Interpreter Callback Demo

+ +

A native function can take a callback as an argument, and call it later + to create real asynchronous scripts. The example below (see this page's + source) creates a getXhr function that fetches the URL and + calls the given callback, returning the content.

+ +

This function is defined using createNativeFunction during + initialization. When called, the interpreter passes the provided callback + function as a pseudo object representing all the data needed to execute + it later. The Xhr is then intialized and waits for the targeted URL's content. + The execution of the interpreter's program continues normally. When the + Xhr responds, the callback is loaded in the interpeter, and all it's arguments + are provided using queueCall. Then the interpreter is re-started + by calling run, and the callback function is executed within the + interpreter.

+ +

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

+ +


+ + + +

+ +

Back to the JS-Interpreter documentation.

+ + + + diff --git a/docs.html b/docs.html index 94e7c325..ec7fe96c 100644 --- a/docs.html +++ b/docs.html @@ -140,6 +140,28 @@

External API

For a working example, see the async demo.

+

Alternatively, a callback can be provided to make asychronous calls. The + getXhr(url) function can therefore be written like this :

+ +
+    var wrapper = function(href, callback) {
+      href = href ? href.toString() : '';
+      var req = new XMLHttpRequest();
+      req.open('GET', href, true);
+      req.onreadystatechange = function() {
+        if (req.readyState == 4 && req.status == 200) {
+          interpreter.queueCall(callback, [interpreter.createPrimitive(req.responseText)]);
+          interpreter.run();
+        }
+      };
+      req.send(null);
+    };
+    interpreter.setProperty(scope, 'getXhr',
+        interpreter.createNativeFunction(wrapper));
+  
+ +

See the callback demo.

+

Limitations

The version of JavaScript implemented by the interpreter has a few diff --git a/interpreter.js b/interpreter.js index e6dcb056..256dabcb 100644 --- a/interpreter.js +++ b/interpreter.js @@ -143,6 +143,41 @@ Interpreter.prototype.appendCode = function(code) { state.done = false; }; +/** +* Shifts the given function at the bottom of the state stack, delaying the call. +* @param {Interpreter.Object} func Pseudo function to call. +* @param {Interpreter.Object[]} args Arguments to provide to the function. +*/ +Interpreter.prototype.queueCall = function(func, args) { + var state = this.stateStack[0]; + var interpreter = this; + if (!state || state.node.type != 'Program') { + throw Error('Expecting original AST to start with a Program node.'); + } + state.done = false; + var scope = this.createScope(func.node.body, func.parentScope); + func.node.params.forEach(function(p, i) { + interpreter.setProperty(scope, interpreter.createPrimitive(p.name), args[i]); + }) + var argsList = this.createObject(this.ARRAY); + args.forEach(function(arg, i) { + interpreter.setProperty(argsList, interpreter.createPrimitive(i), arg); + }) + this.setProperty(scope, 'arguments', argsList); + var last = func.node.body.body[func.node.body.body.length - 1]; + if(last.type == 'ReturnStatement') { + last.type = 'ExpressionStatement'; + last.expression = last.argument; + delete last.argument; + } + this.stateStack.splice(1, 0, { + node: func.node.body, + scope: scope, + arguments: [this.createPrimitive('Hello !')], + value: undefined + }); +}; + /** * Execute one step of the interpreter. * @return {boolean} True if a step was executed, false if no more instructions. @@ -3738,3 +3773,4 @@ Interpreter.prototype['createAsyncFunction'] = Interpreter.prototype.createAsyncFunction; Interpreter.prototype['step'] = Interpreter.prototype.step; Interpreter.prototype['run'] = Interpreter.prototype.run; +Interpreter.prototype['queueCall'] = Interpreter.prototype.queueCall; From bdf7928905fc4352a85f24dbf216fb3e7a4dc783 Mon Sep 17 00:00:00 2001 From: thejohncrafter Date: Sun, 14 May 2017 15:08:34 +0200 Subject: [PATCH 2/3] Strict mode support. --- interpreter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter.js b/interpreter.js index 256dabcb..17cbc840 100644 --- a/interpreter.js +++ b/interpreter.js @@ -174,7 +174,7 @@ Interpreter.prototype.queueCall = function(func, args) { node: func.node.body, scope: scope, arguments: [this.createPrimitive('Hello !')], - value: undefined + value: this.getScope().strict ? this.UNDEFINED : this.global }); }; From 662379a855c9025b2e913b583fc36b103eeb0982 Mon Sep 17 00:00:00 2001 From: thejohncrafter Date: Sat, 27 May 2017 16:25:37 +0200 Subject: [PATCH 3/3] Removed forgotten line. --- interpreter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/interpreter.js b/interpreter.js index 17cbc840..a62471c7 100644 --- a/interpreter.js +++ b/interpreter.js @@ -173,7 +173,6 @@ Interpreter.prototype.queueCall = function(func, args) { this.stateStack.splice(1, 0, { node: func.node.body, scope: scope, - arguments: [this.createPrimitive('Hello !')], value: this.getScope().strict ? this.UNDEFINED : this.global }); };