You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
6.8 KiB
JavaScript

6 years ago
var asyncExit = /^async[\t ]+(return|throw)/ ;
var atomOrPropertyOrLabel = /^\s*[):;]/ ;
var removeComments = /([^\n])\/\*(\*(?!\/)|[^\n*])*\*\/([^\n])/g ;
function hasLineTerminatorBeforeNext(st, since) {
return st.lineStart >= since;
}
function test(regex,st,noComment) {
var src = st.input.slice(st.start) ;
if (noComment) {
src = src.replace(removeComments,"$1 $3") ;
}
return regex.test(src);
}
/* Create a new parser derived from the specified parser, so that in the
* event of an error we can back out and try again */
function subParse(parser, pos, extensions) {
var p = new parser.constructor(parser.options, parser.input, pos);
if (extensions)
for (var k in extensions)
p[k] = extensions[k] ;
var src = parser ;
var dest = p ;
['inFunction','inAsync','inGenerator','inModule'].forEach(function(k){
if (k in src)
dest[k] = src[k] ;
}) ;
p.nextToken();
return p;
}
function asyncAwaitPlugin (parser,options){
if (!options || typeof options !== "object")
options = {} ;
parser.extend("parse",function(base){
return function(){
this.inAsync = options.inAsyncFunction ;
if (options.awaitAnywhere && options.inAsyncFunction)
parser.raise(node.start,"The options awaitAnywhere and inAsyncFunction are mutually exclusive") ;
return base.apply(this,arguments);
}
}) ;
parser.extend("parseStatement",function(base){
return function (declaration, topLevel) {
var start = this.start;
var startLoc = this.startLoc;
if (this.type.label==='name') {
if ((options.asyncExits) && test(asyncExit,this)) {
// TODO: Ensure this function is itself nested in an async function or Method
this.next() ;
var r = this.parseStatement(declaration, topLevel) ;
r.async = true ;
r.start = start;
r.loc && (r.loc.start = startLoc);
r.range && (r.range[0] = start);
return r ;
}
}
return base.apply(this,arguments);
}
}) ;
parser.extend("parseIdent",function(base){
return function(liberal) {
if (this.options.sourceType==='module' && this.options.ecmaVersion >= 8 && options.awaitAnywhere)
return base.call(this,true) ; // Force liberal mode if awaitAnywhere is set
return base.apply(this,arguments) ;
}
}) ;
parser.extend("parseExprAtom",function(base){
var NotAsync = {};
return function(refShorthandDefaultPos){
var start = this.start ;
var startLoc = this.startLoc;
var rhs,r = base.apply(this,arguments);
if (r.type==='Identifier') {
if (r.name==='await' && !this.inAsync) {
if (options.awaitAnywhere) {
var n = this.startNodeAt(r.start, r.loc && r.loc.start);
start = this.start ;
var parseHooks = {
raise:function(){
try {
return pp.raise.apply(this,arguments) ;
} catch(ex) {
throw /*inBody?ex:*/NotAsync ;
}
}
} ;
try {
rhs = subParse(this,start-4,parseHooks).parseExprSubscripts() ;
if (rhs.end<=start) {
rhs = subParse(this,start,parseHooks).parseExprSubscripts() ;
n.argument = rhs ;
n = this.finishNodeAt(n,'AwaitExpression', rhs.end, rhs.loc && rhs.loc.end) ;
this.pos = rhs.end;
this.end = rhs.end ;
this.endLoc = rhs.endLoc ;
this.next();
return n ;
}
} catch (ex) {
if (ex===NotAsync)
return r ;
throw ex ;
}
}
}
}
return r ;
}
}) ;
var allowedPropValues = {
undefined:true,
get:true,
set:true,
static:true,
async:true,
constructor:true
};
parser.extend("parsePropertyName",function(base){
return function (prop) {
var prevName = prop.key && prop.key.name ;
var key = base.apply(this,arguments) ;
if (this.value==='get') {
prop.__maybeStaticAsyncGetter = true ;
}
var next ;
if (allowedPropValues[this.value])
return key ;
if (key.type === "Identifier" && (key.name === "async" || prevName === "async") && !hasLineTerminatorBeforeNext(this, key.end)
// Look-ahead to see if this is really a property or label called async or await
&& !this.input.slice(key.end).match(atomOrPropertyOrLabel)) {
if (prop.kind === 'set' || key.name === 'set')
this.raise(key.start,"'set <member>(value)' cannot be be async") ;
else {
this.__isAsyncProp = true ;
key = base.apply(this,arguments) ;
if (key.type==='Identifier') {
if (key.name==='set')
this.raise(key.start,"'set <member>(value)' cannot be be async") ;
}
}
} else {
delete prop.__maybeStaticAsyncGetter ;
}
return key;
};
}) ;
parser.extend("parseClassMethod",function(base){
return function (classBody, method, isGenerator) {
var r = base.apply(this,arguments) ;
if (method.__maybeStaticAsyncGetter) {
delete method.__maybeStaticAsyncGetter ;
if (method.key.name!=='get')
method.kind = "get" ;
}
return r ;
}
}) ;
parser.extend("parseFunctionBody",function(base){
return function (node, isArrowFunction) {
var wasAsync = this.inAsync ;
if (this.__isAsyncProp) {
node.async = true ;
this.inAsync = true ;
delete this.__isAsyncProp ;
}
var r = base.apply(this,arguments) ;
this.inAsync = wasAsync ;
return r ;
}
}) ;
}
module.exports = asyncAwaitPlugin ;