acorn-v4.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. var asyncExit = /^async[\t ]+(return|throw)/ ;
  2. var atomOrPropertyOrLabel = /^\s*[):;]/ ;
  3. var removeComments = /([^\n])\/\*(\*(?!\/)|[^\n*])*\*\/([^\n])/g ;
  4. function hasLineTerminatorBeforeNext(st, since) {
  5. return st.lineStart >= since;
  6. }
  7. function test(regex,st,noComment) {
  8. var src = st.input.slice(st.start) ;
  9. if (noComment) {
  10. src = src.replace(removeComments,"$1 $3") ;
  11. }
  12. return regex.test(src);
  13. }
  14. /* Create a new parser derived from the specified parser, so that in the
  15. * event of an error we can back out and try again */
  16. function subParse(parser, pos, extensions) {
  17. var p = new parser.constructor(parser.options, parser.input, pos);
  18. if (extensions)
  19. for (var k in extensions)
  20. p[k] = extensions[k] ;
  21. var src = parser ;
  22. var dest = p ;
  23. ['inFunction','inAsync','inGenerator','inModule'].forEach(function(k){
  24. if (k in src)
  25. dest[k] = src[k] ;
  26. }) ;
  27. p.nextToken();
  28. return p;
  29. }
  30. function asyncAwaitPlugin (parser,options){
  31. if (!options || typeof options !== "object")
  32. options = {} ;
  33. parser.extend("parse",function(base){
  34. return function(){
  35. this.inAsync = options.inAsyncFunction ;
  36. if (options.awaitAnywhere && options.inAsyncFunction)
  37. parser.raise(node.start,"The options awaitAnywhere and inAsyncFunction are mutually exclusive") ;
  38. return base.apply(this,arguments);
  39. }
  40. }) ;
  41. parser.extend("parseStatement",function(base){
  42. return function (declaration, topLevel) {
  43. var start = this.start;
  44. var startLoc = this.startLoc;
  45. if (this.type.label==='name') {
  46. if ((options.asyncExits) && test(asyncExit,this)) {
  47. // TODO: Ensure this function is itself nested in an async function or Method
  48. this.next() ;
  49. var r = this.parseStatement(declaration, topLevel) ;
  50. r.async = true ;
  51. r.start = start;
  52. r.loc && (r.loc.start = startLoc);
  53. r.range && (r.range[0] = start);
  54. return r ;
  55. }
  56. }
  57. return base.apply(this,arguments);
  58. }
  59. }) ;
  60. parser.extend("parseIdent",function(base){
  61. return function(liberal) {
  62. if (this.options.sourceType==='module' && this.options.ecmaVersion >= 8 && options.awaitAnywhere)
  63. return base.call(this,true) ; // Force liberal mode if awaitAnywhere is set
  64. return base.apply(this,arguments) ;
  65. }
  66. }) ;
  67. parser.extend("parseExprAtom",function(base){
  68. var NotAsync = {};
  69. return function(refShorthandDefaultPos){
  70. var start = this.start ;
  71. var startLoc = this.startLoc;
  72. var rhs,r = base.apply(this,arguments);
  73. if (r.type==='Identifier') {
  74. if (r.name==='await' && !this.inAsync) {
  75. if (options.awaitAnywhere) {
  76. var n = this.startNodeAt(r.start, r.loc && r.loc.start);
  77. start = this.start ;
  78. var parseHooks = {
  79. raise:function(){
  80. try {
  81. return pp.raise.apply(this,arguments) ;
  82. } catch(ex) {
  83. throw /*inBody?ex:*/NotAsync ;
  84. }
  85. }
  86. } ;
  87. try {
  88. rhs = subParse(this,start-4,parseHooks).parseExprSubscripts() ;
  89. if (rhs.end<=start) {
  90. rhs = subParse(this,start,parseHooks).parseExprSubscripts() ;
  91. n.argument = rhs ;
  92. n = this.finishNodeAt(n,'AwaitExpression', rhs.end, rhs.loc && rhs.loc.end) ;
  93. this.pos = rhs.end;
  94. this.end = rhs.end ;
  95. this.endLoc = rhs.endLoc ;
  96. this.next();
  97. return n ;
  98. }
  99. } catch (ex) {
  100. if (ex===NotAsync)
  101. return r ;
  102. throw ex ;
  103. }
  104. }
  105. }
  106. }
  107. return r ;
  108. }
  109. }) ;
  110. var allowedPropValues = {
  111. undefined:true,
  112. get:true,
  113. set:true,
  114. static:true,
  115. async:true,
  116. constructor:true
  117. };
  118. parser.extend("parsePropertyName",function(base){
  119. return function (prop) {
  120. var prevName = prop.key && prop.key.name ;
  121. var key = base.apply(this,arguments) ;
  122. if (this.value==='get') {
  123. prop.__maybeStaticAsyncGetter = true ;
  124. }
  125. var next ;
  126. if (allowedPropValues[this.value])
  127. return key ;
  128. if (key.type === "Identifier" && (key.name === "async" || prevName === "async") && !hasLineTerminatorBeforeNext(this, key.end)
  129. // Look-ahead to see if this is really a property or label called async or await
  130. && !this.input.slice(key.end).match(atomOrPropertyOrLabel)) {
  131. if (prop.kind === 'set' || key.name === 'set')
  132. this.raise(key.start,"'set <member>(value)' cannot be be async") ;
  133. else {
  134. this.__isAsyncProp = true ;
  135. key = base.apply(this,arguments) ;
  136. if (key.type==='Identifier') {
  137. if (key.name==='set')
  138. this.raise(key.start,"'set <member>(value)' cannot be be async") ;
  139. }
  140. }
  141. } else {
  142. delete prop.__maybeStaticAsyncGetter ;
  143. }
  144. return key;
  145. };
  146. }) ;
  147. parser.extend("parseClassMethod",function(base){
  148. return function (classBody, method, isGenerator) {
  149. var r = base.apply(this,arguments) ;
  150. if (method.__maybeStaticAsyncGetter) {
  151. delete method.__maybeStaticAsyncGetter ;
  152. if (method.key.name!=='get')
  153. method.kind = "get" ;
  154. }
  155. return r ;
  156. }
  157. }) ;
  158. parser.extend("parseFunctionBody",function(base){
  159. return function (node, isArrowFunction) {
  160. var wasAsync = this.inAsync ;
  161. if (this.__isAsyncProp) {
  162. node.async = true ;
  163. this.inAsync = true ;
  164. delete this.__isAsyncProp ;
  165. }
  166. var r = base.apply(this,arguments) ;
  167. this.inAsync = wasAsync ;
  168. return r ;
  169. }
  170. }) ;
  171. }
  172. module.exports = asyncAwaitPlugin ;