Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
409 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,3 +35,4 @@ jspm_packages | |
|
||
# Optional REPL history | ||
.node_repl_history | ||
/.project |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "nodent-runtime", | ||
"version": "3.0.0", | ||
"description": "Runtime component of nodent", | ||
"main": "runtime.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/MatAtBread/nodent-runtime.git" | ||
}, | ||
"keywords": [ | ||
"nodent", | ||
"runtime", | ||
"asunc", | ||
"await", | ||
"es7" | ||
], | ||
"author": "matatbread", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/MatAtBread/nodent-runtime/issues" | ||
}, | ||
"homepage": "https://github.com/MatAtBread/nodent-runtime#readme" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
"use strict"; | ||
/* | ||
* $asyncbind has multiple uses, depending on the parameter list. It is in Function.prototype, so 'this' is always a function | ||
* | ||
* 1) If called with a single argument (this), it is used when defining an async function to ensure when | ||
* it is invoked, the correct 'this' is present, just like "bind". For legacy reasons, 'this' is given | ||
* a memeber 'then' which refers to itself. | ||
* 2) If called with a second parameter ("catcher") and catcher!==true it is being used to invoke an async | ||
* function where the second parameter is the error callback (for sync exceptions and to be passed to | ||
* nested async calls) | ||
* 3) If called with the second parameter===true, it is the same use as (1), but the function is wrapped | ||
* in an 'Promise' as well bound to 'this'. | ||
* It is the same as calling 'new Promise(this)', where 'this' is the function being bound/wrapped | ||
* 4) If called with the second parameter===0, it is the same use as (1), but the function is wrapped | ||
* in a 'LazyThenable', which executes lazily and can resolve synchronously. | ||
* It is the same as calling 'new LazyThenable(this)' (if such a type were exposed), where 'this' is | ||
* the function being bound/wrapped | ||
*/ | ||
|
||
function processIncludes(includes,input) { | ||
var src = input.toString() ; | ||
var t = "return "+src ; | ||
var args = src.match(/.*\(([^)]*)\)/)[1] ; | ||
var re = /!!!'([^']*)'/g ; | ||
var m = [] ; | ||
while (1) { | ||
var mx = re.exec(t) ; | ||
if (mx) | ||
m.push(mx) ; | ||
else break ; | ||
} | ||
m.reverse().forEach(function(e){ | ||
t = t.slice(0,e.index)+includes[e[1]]+t.substr(e.index+e[0].length) ; | ||
}) ; | ||
t = t.replace(/\/\*[^*]*\*\//g,' ').replace(/\s+/g,' ') ; | ||
return new Function(args,t)() ; | ||
} | ||
|
||
var $asyncbind = processIncludes({ | ||
zousan:require('./zousan').toString(), | ||
thenable:require('./thenableFactory').toString() | ||
}, | ||
function $asyncbind(self,catcher) { | ||
"use strict"; | ||
if (!Function.prototype.$asyncbind) { | ||
Object.defineProperty(Function.prototype,"$asyncbind",{value:$asyncbind,enumerable:false,configurable:true,writable:true}) ; | ||
} | ||
|
||
if (!$asyncbind.trampoline) { | ||
$asyncbind.trampoline = function trampoline(t,x,s,e){ | ||
return function b(q) { | ||
while (q) { | ||
if (q.then) | ||
return q.then(b, e); | ||
try { | ||
if (q.pop) { | ||
if (q.length) | ||
return q.pop() ? x.call(t) : q; | ||
q = s; | ||
} else | ||
q = q.call(t) | ||
} catch (r) { | ||
return e(r); | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
if (!$asyncbind.LazyThenable) { | ||
$asyncbind.LazyThenable = !!!'thenable'(); | ||
$asyncbind.EagerThenable = $asyncbind.Thenable = ($asyncbind.EagerThenableFactory = !!!'zousan')(); | ||
} | ||
|
||
var resolver = this; | ||
switch (catcher) { | ||
case true: | ||
return new ($asyncbind.Thenable)(boundThen); | ||
case 0: | ||
return new ($asyncbind.LazyThenable)(boundThen); | ||
case undefined: | ||
/* For runtime compatibility with Nodent v2.x, provide a thenable */ | ||
boundThen.then = boundThen ; | ||
return boundThen ; | ||
default: | ||
return function(){ | ||
try { | ||
return resolver.apply(self,arguments); | ||
} catch(ex) { | ||
return catcher(ex); | ||
} | ||
} | ||
} | ||
function boundThen() { | ||
return resolver.apply(self,arguments); | ||
} | ||
}) ; | ||
|
||
function $asyncspawn(promiseProvider,self) { | ||
if (!Function.prototype.$asyncspawn) { | ||
Object.defineProperty(Function.prototype,"$asyncspawn",{value:$asyncspawn,enumerable:false,configurable:true,writable:true}) ; | ||
} | ||
if (!(this instanceof Function)) return ; | ||
|
||
var genF = this ; | ||
return new promiseProvider(function enough(resolve, reject) { | ||
var gen = genF.call(self, resolve, reject); | ||
function step(fn,arg) { | ||
var next; | ||
try { | ||
next = fn.call(gen,arg); | ||
if(next.done) { | ||
if (next.value !== resolve) { | ||
if (next.value && next.value===next.value.then) | ||
return next.value(resolve,reject) ; | ||
resolve && resolve(next.value); | ||
resolve = null ; | ||
} | ||
return; | ||
} | ||
|
||
if (next.value.then) { | ||
next.value.then(function(v) { | ||
step(gen.next,v); | ||
}, function(e) { | ||
step(gen.throw,e); | ||
}); | ||
} else { | ||
step(gen.next,next.value); | ||
} | ||
} catch(e) { | ||
reject && reject(e); | ||
reject = null ; | ||
return; | ||
} | ||
} | ||
step(gen.next); | ||
}); | ||
} | ||
|
||
// Initialize async bindings | ||
$asyncbind() ; | ||
$asyncspawn() ; | ||
|
||
// Export async bindings | ||
module.exports = { | ||
$asyncbind:$asyncbind, | ||
$asyncspawn:$asyncspawn | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
function Thenable(thenable) { | ||
return thenable.then = thenable ; | ||
}; | ||
|
||
Thenable.resolve = function(v){ | ||
return Thenable.isThenable(v) ? v : {then:function(resolve){return resolve(v)}}; | ||
}; | ||
|
||
Thenable.isThenable = function(obj) { | ||
return obj && (obj instanceof Object) && typeof obj.then==="function"; | ||
} | ||
|
||
module.exports = Thenable ; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
module.exports = function() { | ||
function isThenable(obj) { | ||
return obj && (obj instanceof Object) && typeof obj.then==="function"; | ||
} | ||
|
||
function resolution(p,r,how) { | ||
try { | ||
/* 2.2.7.1 */ | ||
var x = how ? how(r):r ; | ||
|
||
if (p===x) /* 2.3.1 */ | ||
return p.reject(new TypeError("Promise resolution loop")) ; | ||
|
||
if (isThenable(x)) { | ||
/* 2.3.3 */ | ||
x.then(function(y){ | ||
resolution(p,y); | ||
},function(e){ | ||
p.reject(e) | ||
}) ; | ||
} else { | ||
p.resolve(x) ; | ||
} | ||
} catch (ex) { | ||
/* 2.2.7.2 */ | ||
p.reject(ex) ; | ||
} | ||
} | ||
|
||
function Chained() {}; | ||
Chained.prototype = { | ||
resolve:_unchained, | ||
reject:_unchained, | ||
then:thenChain | ||
}; | ||
function _unchained(v){} | ||
function thenChain(res,rej){ | ||
this.resolve = res; | ||
this.reject = rej; | ||
} | ||
|
||
function then(res,rej){ | ||
var chain = new Chained() ; | ||
try { | ||
this._resolver(function(value) { | ||
return isThenable(value) ? value.then(res,rej) : resolution(chain,value,res); | ||
},function(ex) { | ||
resolution(chain,ex,rej) ; | ||
}) ; | ||
} catch (ex) { | ||
resolution(chain,ex,rej); | ||
} | ||
return chain ; | ||
} | ||
|
||
function Thenable(resolver) { | ||
this._resolver = resolver ; | ||
this.then = then ; | ||
}; | ||
|
||
Thenable.resolve = function(v){ | ||
return Thenable.isThenable(v) ? v : {then:function(resolve){return resolve(v)}}; | ||
}; | ||
|
||
Thenable.isThenable = isThenable ; | ||
|
||
return Thenable ; | ||
} ; |
Oops, something went wrong.