123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- var NotAsync = {} ;
- var asyncExit = /^async[\t ]+(return|throw)/ ;
- var asyncFunction = /^async[\t ]+function/ ;
- var atomOrPropertyOrLabel = /^\s*[():;]/ ;
- var removeComments = /([^\n])\/\*(\*(?!\/)|[^\n*])*\*\/([^\n])/g ;
- var matchAsyncGet = /\s*(get|set)\s*\(/ ;
- 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,parens) {
- 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','inAsyncFunction','inAsync','inGenerator','inModule'].forEach(function(k){
- if (k in src)
- dest[k] = src[k] ;
- }) ;
- if (parens)
- p.options.preserveParens = true ;
- p.nextToken();
- return p;
- }
- //TODO: Implement option noAsyncGetters
- function asyncAwaitPlugin (parser,options){
- var es7check = function(){} ;
- parser.extend("initialContext",function(base){
- return function(){
- if (this.options.ecmaVersion < 7) {
- es7check = function(node) {
- parser.raise(node.start,"async/await keywords only available when ecmaVersion>=7") ;
- } ;
- }
- this.reservedWords = new RegExp(this.reservedWords.toString().replace(/await|async/g,"").replace("|/","/").replace("/|","/").replace("||","|")) ;
- this.reservedWordsStrict = new RegExp(this.reservedWordsStrict.toString().replace(/await|async/g,"").replace("|/","/").replace("/|","/").replace("||","|")) ;
- this.reservedWordsStrictBind = new RegExp(this.reservedWordsStrictBind.toString().replace(/await|async/g,"").replace("|/","/").replace("/|","/").replace("||","|")) ;
- this.inAsyncFunction = 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("shouldParseExportStatement",function(base){
- return function(){
- if (this.type.label==='name' && this.value==='async' && test(asyncFunction,this)) {
- return true ;
- }
- 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 (test(asyncFunction,this,true)) {
- var wasAsync = this.inAsyncFunction ;
- try {
- this.inAsyncFunction = true ;
- 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 ;
- } finally {
- this.inAsyncFunction = wasAsync ;
- }
- } else if ((typeof options==="object" && options.asyncExits) && test(asyncExit,this)) {
- // NON-STANDARD EXTENSION iff. options.asyncExits is set, the
- // extensions 'async return <expr>?' and 'async throw <expr>?'
- // are enabled. In each case they are the standard ESTree nodes
- // with the flag 'async:true'
- 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){
- var id = base.apply(this,arguments);
- if (this.inAsyncFunction && id.name==='await') {
- if (arguments.length===0) {
- this.raise(id.start,"'await' is reserved within async functions") ;
- }
- }
- return id ;
- }
- }) ;
- parser.extend("parseExprAtom",function(base){
- 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==='async' && !hasLineTerminatorBeforeNext(this, r.end)) {
- // Is this really an async function?
- var isAsync = this.inAsyncFunction ;
- try {
- this.inAsyncFunction = true ;
- var pp = this ;
- var inBody = false ;
- var parseHooks = {
- parseFunctionBody:function(node,isArrowFunction){
- try {
- var wasInBody = inBody ;
- inBody = true ;
- return pp.parseFunctionBody.apply(this,arguments) ;
- } finally {
- inBody = wasInBody ;
- }
- },
- raise:function(){
- try {
- return pp.raise.apply(this,arguments) ;
- } catch(ex) {
- throw inBody?ex:NotAsync ;
- }
- }
- } ;
- rhs = subParse(this,this.start,parseHooks,true).parseExpression() ;
- if (rhs.type==='SequenceExpression')
- rhs = rhs.expressions[0] ;
- if (rhs.type === 'CallExpression')
- rhs = rhs.callee ;
- if (rhs.type==='FunctionExpression' || rhs.type==='FunctionDeclaration' || rhs.type==='ArrowFunctionExpression') {
- // Because we don't know if the top level parser supprts preserveParens, we have to re-parse
- // without it set
- rhs = subParse(this,this.start,parseHooks).parseExpression() ;
- if (rhs.type==='SequenceExpression')
- rhs = rhs.expressions[0] ;
- if (rhs.type === 'CallExpression')
- rhs = rhs.callee ;
-
- rhs.async = true ;
- rhs.start = start;
- rhs.loc && (rhs.loc.start = startLoc);
- rhs.range && (rhs.range[0] = start);
- this.pos = rhs.end;
- this.end = rhs.end ;
- this.endLoc = rhs.endLoc ;
- this.next();
- es7check(rhs) ;
- return rhs ;
- }
- } catch (ex) {
- if (ex!==NotAsync)
- throw ex ;
- }
- finally {
- this.inAsyncFunction = isAsync ;
- }
- }
- else if (r.name==='await') {
- var n = this.startNodeAt(r.start, r.loc && r.loc.start);
- if (this.inAsyncFunction) {
- rhs = this.parseExprSubscripts() ;
- n.operator = 'await' ;
- n.argument = rhs ;
- n = this.finishNodeAt(n,'AwaitExpression', rhs.end, rhs.loc && rhs.loc.end) ;
- es7check(n) ;
- return n ;
- }
- // NON-STANDARD EXTENSION iff. options.awaitAnywhere is true,
- // an 'AwaitExpression' is allowed anywhere the token 'await'
- // could not be an identifier with the name 'await'.
- // Look-ahead to see if this is really a property or label called async or await
- if (this.input.slice(r.end).match(atomOrPropertyOrLabel)) {
- if (!options.awaitAnywhere && this.options.sourceType === 'module')
- return this.raise(r.start,"'await' is reserved within modules") ;
- return r ; // This is a valid property name or label
- }
- if (typeof options==="object" && options.awaitAnywhere) {
- start = this.start ;
- rhs = subParse(this,start-4).parseExprSubscripts() ;
- if (rhs.end<=start) {
- rhs = subParse(this,start).parseExprSubscripts() ;
- n.operator = 'await' ;
- 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();
- es7check(n) ;
- return n ;
- }
- }
- if (!options.awaitAnywhere && this.options.sourceType === 'module')
- return this.raise(r.start,"'await' is reserved within modules") ;
- }
- }
- return r ;
- }
- }) ;
- parser.extend('finishNodeAt',function(base){
- return function(node,type,pos,loc) {
- if (node.__asyncValue) {
- delete node.__asyncValue ;
- node.value.async = true ;
- }
- return base.apply(this,arguments) ;
- }
- }) ;
- parser.extend('finishNode',function(base){
- return function(node,type) {
- if (node.__asyncValue) {
- delete node.__asyncValue ;
- node.value.async = true ;
- }
- return base.apply(this,arguments) ;
- }
- }) ;
- var allowedPropSpecifiers = {
- get:true,
- set:true,
- async:true
- };
-
- parser.extend("parsePropertyName",function(base){
- return function (prop) {
- var prevName = prop.key && prop.key.name ;
- var key = base.apply(this,arguments) ;
- if (key.type === "Identifier" && (key.name === "async") && !hasLineTerminatorBeforeNext(this, key.end)) {
- // Look-ahead to see if this is really a property or label called async or await
- if (!this.input.slice(key.end).match(atomOrPropertyOrLabel)){
- // Cheese - eliminate the cases 'async get(){}' and async set(){}'
- if (matchAsyncGet.test(this.input.slice(key.end))) {
- key = base.apply(this,arguments) ;
- prop.__asyncValue = true ;
- } else {
- es7check(prop) ;
- if (prop.kind === 'set')
- this.raise(key.start,"'set <member>(value)' cannot be be async") ;
-
- key = base.apply(this,arguments) ;
- if (key.type==='Identifier') {
- if (key.name==='set')
- this.raise(key.start,"'set <member>(value)' cannot be be async") ;
- }
- prop.__asyncValue = true ;
- }
- }
- }
- return key;
- };
- }) ;
- parser.extend("parseClassMethod",function(base){
- return function (classBody, method, isGenerator) {
- var wasAsync ;
- if (method.__asyncValue) {
- if (method.kind==='constructor')
- this.raise(method.start,"class constructor() cannot be be async") ;
- wasAsync = this.inAsyncFunction ;
- this.inAsyncFunction = true ;
- }
- var r = base.apply(this,arguments) ;
- this.inAsyncFunction = wasAsync ;
- return r ;
- }
- }) ;
- parser.extend("parseMethod",function(base){
- return function (isGenerator) {
- var wasAsync ;
- if (this.__currentProperty && this.__currentProperty.__asyncValue) {
- wasAsync = this.inAsyncFunction ;
- this.inAsyncFunction = true ;
- }
- var r = base.apply(this,arguments) ;
- this.inAsyncFunction = wasAsync ;
- return r ;
- }
- }) ;
- parser.extend("parsePropertyValue",function(base){
- return function (prop, isPattern, isGenerator, startPos, startLoc, refDestructuringErrors) {
- var prevProp = this.__currentProperty ;
- this.__currentProperty = prop ;
- var wasAsync ;
- if (prop.__asyncValue) {
- wasAsync = this.inAsyncFunction ;
- this.inAsyncFunction = true ;
- }
- var r = base.apply(this,arguments) ;
- this.inAsyncFunction = wasAsync ;
- this.__currentProperty = prevProp ;
- return r ;
- }
- }) ;
- }
- module.exports = asyncAwaitPlugin ;
|