123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- 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 ;
|