Upgrade to https://github.com/acornjs/acorn.git commit 84eda6bf
[jst.git] / src / statement.js
1 import {types as tt} from "./tokentype.js"
2 import {Parser} from "./state.js"
3 import {lineBreak, skipWhiteSpace} from "./whitespace.js"
4 import {isIdentifierStart, isIdentifierChar, keywordRelationalOperator} from "./identifier.js"
5 import {hasOwn, loneSurrogate} from "./util.js"
6 import {DestructuringErrors} from "./parseutil.js"
7 import {functionFlags, SCOPE_SIMPLE_CATCH, BIND_SIMPLE_CATCH, BIND_LEXICAL, BIND_VAR, BIND_FUNCTION, SCOPE_CLASS_STATIC_BLOCK, SCOPE_SUPER} from "./scopeflags.js"
8
9 const pp = Parser.prototype
10
11 // ### Statement parsing
12
13 // Parse a program. Initializes the parser, reads any number of
14 // statements, and wraps them in a Program node.  Optionally takes a
15 // `program` argument.  If present, the statements will be appended
16 // to its body instead of creating a new node.
17
18 pp.parseTopLevel = function(node) {
19   let exports = Object.create(null)
20   if (!node.body) node.body = []
21   while (this.type !== tt.eof) {
22     let stmt = this.parseStatement(null, true, exports)
23     node.body.push(stmt)
24   }
25   if (this.inModule)
26     for (let name of Object.keys(this.undefinedExports))
27       this.raiseRecoverable(this.undefinedExports[name].start, `Export '${name}' is not defined`)
28   this.adaptDirectivePrologue(node.body)
29   this.next()
30   node.sourceType = this.options.sourceType
31   return this.finishNode(node, "Program")
32 }
33
34 const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}
35
36 pp.isLet = function(context) {
37   if (this.options.ecmaVersion < 6 || !this.isContextual("let")) return false
38   skipWhiteSpace.lastIndex = this.pos
39   let skip = skipWhiteSpace.exec(this.input)
40   let next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next)
41   // For ambiguous cases, determine if a LexicalDeclaration (or only a
42   // Statement) is allowed here. If context is not empty then only a Statement
43   // is allowed. However, `let [` is an explicit negative lookahead for
44   // ExpressionStatement, so special-case it first.
45   if (nextCh === 91 || nextCh === 92 || nextCh > 0xd7ff && nextCh < 0xdc00) return true // '[', '/', astral
46   if (context) return false
47
48   if (nextCh === 123) return true // '{'
49   if (isIdentifierStart(nextCh, true)) {
50     let pos = next + 1
51     while (isIdentifierChar(nextCh = this.input.charCodeAt(pos), true)) ++pos
52     if (nextCh === 92 || nextCh > 0xd7ff && nextCh < 0xdc00) return true
53     let ident = this.input.slice(next, pos)
54     if (!keywordRelationalOperator.test(ident)) return true
55   }
56   return false
57 }
58
59 // check 'async [no LineTerminator here] function'
60 // - 'async /*foo*/ function' is OK.
61 // - 'async /*\n*/ function' is invalid.
62 pp.isAsyncFunction = function() {
63   if (this.options.ecmaVersion < 8 || !this.isContextual("async"))
64     return false
65
66   skipWhiteSpace.lastIndex = this.pos
67   let skip = skipWhiteSpace.exec(this.input)
68   let next = this.pos + skip[0].length, after
69   return !lineBreak.test(this.input.slice(this.pos, next)) &&
70     this.input.slice(next, next + 8) === "function" &&
71     (next + 8 === this.input.length ||
72      !(isIdentifierChar(after = this.input.charCodeAt(next + 8)) || after > 0xd7ff && after < 0xdc00))
73 }
74
75 // Parse a single statement.
76 //
77 // If expecting a statement and finding a slash operator, parse a
78 // regular expression literal. This is to handle cases like
79 // `if (foo) /blah/.exec(foo)`, where looking at the previous token
80 // does not help.
81
82 pp.parseStatement = function(context, topLevel, exports) {
83   let starttype = this.type, node = this.startNode(), kind
84
85   if (this.isLet(context)) {
86     starttype = tt._var
87     kind = "let"
88   }
89
90   // Most types of statements are recognized by the keyword they
91   // start with. Many are trivial to parse, some require a bit of
92   // complexity.
93
94   switch (starttype) {
95   case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword)
96   case tt._debugger: return this.parseDebuggerStatement(node)
97   case tt._do: return this.parseDoStatement(node)
98   case tt._for: return this.parseForStatement(node)
99   case tt._function:
100     // Function as sole body of either an if statement or a labeled statement
101     // works, but not when it is part of a labeled statement that is the sole
102     // body of an if statement.
103     if ((context && (this.strict || context !== "if" && context !== "label")) && this.options.ecmaVersion >= 6) this.unexpected()
104     return this.parseFunctionStatement(node, false, !context)
105   case tt._class:
106     if (context) this.unexpected()
107     return this.parseClass(node, true)
108   case tt._if: return this.parseIfStatement(node)
109   case tt._return: return this.parseReturnStatement(node)
110   case tt._switch: return this.parseSwitchStatement(node)
111   case tt._throw: return this.parseThrowStatement(node)
112   case tt._try: return this.parseTryStatement(node)
113   case tt._const: case tt._var:
114     kind = kind || this.value
115     if (context && kind !== "var") this.unexpected()
116     return this.parseVarStatement(node, kind)
117   case tt._while: return this.parseWhileStatement(node)
118   case tt._with: return this.parseWithStatement(node)
119   case tt.braceL: return this.parseBlock(true, node)
120   case tt.semi: return this.parseEmptyStatement(node)
121   case tt._export:
122   case tt._import:
123     if (this.options.ecmaVersion > 10 && starttype === tt._import) {
124       skipWhiteSpace.lastIndex = this.pos
125       let skip = skipWhiteSpace.exec(this.input)
126       let next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next)
127       if (nextCh === 40 || nextCh === 46) // '(' or '.'
128         return this.parseExpressionStatement(node, this.parseExpression())
129     }
130
131     if (!this.options.allowImportExportEverywhere) {
132       if (!topLevel)
133         this.raise(this.start, "'import' and 'export' may only appear at the top level")
134       if (!this.inModule)
135         this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'")
136     }
137     return starttype === tt._import ? this.parseImport(node) : this.parseExport(node, exports)
138
139     // If the statement does not start with a statement keyword or a
140     // brace, it's an ExpressionStatement or LabeledStatement. We
141     // simply start parsing an expression, and afterwards, if the
142     // next token is a colon and the expression was a simple
143     // Identifier node, we switch to interpreting it as a label.
144   default:
145     if (this.isAsyncFunction()) {
146       if (context) this.unexpected()
147       this.next()
148       return this.parseFunctionStatement(node, true, !context)
149     }
150
151     let maybeName = this.value, expr = this.parseExpression()
152     if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon))
153       return this.parseLabeledStatement(node, maybeName, expr, context)
154     else return this.parseExpressionStatement(node, expr)
155   }
156 }
157
158 pp.parseBreakContinueStatement = function(node, keyword) {
159   let isBreak = keyword === "break"
160   this.next()
161   if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null
162   else if (this.type !== tt.name) this.unexpected()
163   else {
164     node.label = this.parseIdent()
165     this.semicolon()
166   }
167
168   // Verify that there is an actual destination to break or
169   // continue to.
170   let i = 0
171   for (; i < this.labels.length; ++i) {
172     let lab = this.labels[i]
173     if (node.label == null || lab.name === node.label.name) {
174       if (lab.kind != null && (isBreak || lab.kind === "loop")) break
175       if (node.label && isBreak) break
176     }
177   }
178   if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword)
179   return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
180 }
181
182 pp.parseDebuggerStatement = function(node) {
183   this.next()
184   this.semicolon()
185   return this.finishNode(node, "DebuggerStatement")
186 }
187
188 pp.parseDoStatement = function(node) {
189   this.next()
190   this.labels.push(loopLabel)
191   node.body = this.parseStatement("do")
192   this.labels.pop()
193   this.expect(tt._while)
194   node.test = this.parseParenExpression()
195   if (this.options.ecmaVersion >= 6)
196     this.eat(tt.semi)
197   else
198     this.semicolon()
199   return this.finishNode(node, "DoWhileStatement")
200 }
201
202 // Disambiguating between a `for` and a `for`/`in` or `for`/`of`
203 // loop is non-trivial. Basically, we have to parse the init `var`
204 // statement or expression, disallowing the `in` operator (see
205 // the second parameter to `parseExpression`), and then check
206 // whether the next token is `in` or `of`. When there is no init
207 // part (semicolon immediately after the opening parenthesis), it
208 // is a regular `for` loop.
209
210 pp.parseForStatement = function(node) {
211   this.next()
212   let awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual("await")) ? this.lastTokStart : -1
213   this.labels.push(loopLabel)
214   this.enterScope(0)
215   this.expect(tt.parenL)
216   if (this.type === tt.semi) {
217     if (awaitAt > -1) this.unexpected(awaitAt)
218     return this.parseFor(node, null)
219   }
220   let isLet = this.isLet()
221   if (this.type === tt._var || this.type === tt._const || isLet) {
222     let init = this.startNode(), kind = isLet ? "let" : this.value
223     this.next()
224     this.parseVar(init, true, kind)
225     this.finishNode(init, "VariableDeclaration")
226     if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1) {
227       if (this.options.ecmaVersion >= 9) {
228         if (this.type === tt._in) {
229           if (awaitAt > -1) this.unexpected(awaitAt)
230         } else node.await = awaitAt > -1
231       }
232       return this.parseForIn(node, init)
233     }
234     if (awaitAt > -1) this.unexpected(awaitAt)
235     return this.parseFor(node, init)
236   }
237   let startsWithLet = this.isContextual("let"), isForOf = false
238   let refDestructuringErrors = new DestructuringErrors
239   let init = this.parseExpression(awaitAt > -1 ? "await" : true, refDestructuringErrors)
240   if (this.type === tt._in || (isForOf = this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
241     if (this.options.ecmaVersion >= 9) {
242       if (this.type === tt._in) {
243         if (awaitAt > -1) this.unexpected(awaitAt)
244       } else node.await = awaitAt > -1
245     }
246     if (startsWithLet && isForOf) this.raise(init.start, "The left-hand side of a for-of loop may not start with 'let'.")
247     this.toAssignable(init, false, refDestructuringErrors)
248     this.checkLValPattern(init)
249     return this.parseForIn(node, init)
250   } else {
251     this.checkExpressionErrors(refDestructuringErrors, true)
252   }
253   if (awaitAt > -1) this.unexpected(awaitAt)
254   return this.parseFor(node, init)
255 }
256
257 pp.parseFunctionStatement = function(node, isAsync, declarationPosition) {
258   this.next()
259   return this.parseFunction(node, FUNC_STATEMENT | (declarationPosition ? 0 : FUNC_HANGING_STATEMENT), false, isAsync)
260 }
261
262 pp.parseIfStatement = function(node) {
263   this.next()
264   node.test = this.parseParenExpression()
265   // allow function declarations in branches, but only in non-strict mode
266   node.consequent = this.parseStatement("if")
267   node.alternate = this.eat(tt._else) ? this.parseStatement("if") : null
268   return this.finishNode(node, "IfStatement")
269 }
270
271 pp.parseReturnStatement = function(node) {
272   if (!this.inFunction && !this.options.allowReturnOutsideFunction)
273     this.raise(this.start, "'return' outside of function")
274   this.next()
275
276   // In `return` (and `break`/`continue`), the keywords with
277   // optional arguments, we eagerly look for a semicolon or the
278   // possibility to insert one.
279
280   if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null
281   else { node.argument = this.parseExpression(); this.semicolon() }
282   return this.finishNode(node, "ReturnStatement")
283 }
284
285 pp.parseSwitchStatement = function(node) {
286   this.next()
287   node.discriminant = this.parseParenExpression()
288   node.cases = []
289   this.expect(tt.braceL)
290   this.labels.push(switchLabel)
291   this.enterScope(0)
292
293   // Statements under must be grouped (by label) in SwitchCase
294   // nodes. `cur` is used to keep the node that we are currently
295   // adding statements to.
296
297   let cur
298   for (let sawDefault = false; this.type !== tt.braceR;) {
299     if (this.type === tt._case || this.type === tt._default) {
300       let isCase = this.type === tt._case
301       if (cur) this.finishNode(cur, "SwitchCase")
302       node.cases.push(cur = this.startNode())
303       cur.consequent = []
304       this.next()
305       if (isCase) {
306         cur.test = this.parseExpression()
307       } else {
308         if (sawDefault) this.raiseRecoverable(this.lastTokStart, "Multiple default clauses")
309         sawDefault = true
310         cur.test = null
311       }
312       this.expect(tt.colon)
313     } else {
314       if (!cur) this.unexpected()
315       cur.consequent.push(this.parseStatement(null))
316     }
317   }
318   this.exitScope()
319   if (cur) this.finishNode(cur, "SwitchCase")
320   this.next() // Closing brace
321   this.labels.pop()
322   return this.finishNode(node, "SwitchStatement")
323 }
324
325 pp.parseThrowStatement = function(node) {
326   this.next()
327   if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))
328     this.raise(this.lastTokEnd, "Illegal newline after throw")
329   node.argument = this.parseExpression()
330   this.semicolon()
331   return this.finishNode(node, "ThrowStatement")
332 }
333
334 // Reused empty array added for node fields that are always empty.
335
336 const empty = []
337
338 pp.parseTryStatement = function(node) {
339   this.next()
340   node.block = this.parseBlock()
341   node.handler = null
342   if (this.type === tt._catch) {
343     let clause = this.startNode()
344     this.next()
345     if (this.eat(tt.parenL)) {
346       clause.param = this.parseBindingAtom()
347       let simple = clause.param.type === "Identifier"
348       this.enterScope(simple ? SCOPE_SIMPLE_CATCH : 0)
349       this.checkLValPattern(clause.param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL)
350       this.expect(tt.parenR)
351     } else {
352       if (this.options.ecmaVersion < 10) this.unexpected()
353       clause.param = null
354       this.enterScope(0)
355     }
356     clause.body = this.parseBlock(false)
357     this.exitScope()
358     node.handler = this.finishNode(clause, "CatchClause")
359   }
360   node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null
361   if (!node.handler && !node.finalizer)
362     this.raise(node.start, "Missing catch or finally clause")
363   return this.finishNode(node, "TryStatement")
364 }
365
366 pp.parseVarStatement = function(node, kind) {
367   this.next()
368   this.parseVar(node, false, kind)
369   this.semicolon()
370   return this.finishNode(node, "VariableDeclaration")
371 }
372
373 pp.parseWhileStatement = function(node) {
374   this.next()
375   node.test = this.parseParenExpression()
376   this.labels.push(loopLabel)
377   node.body = this.parseStatement("while")
378   this.labels.pop()
379   return this.finishNode(node, "WhileStatement")
380 }
381
382 pp.parseWithStatement = function(node) {
383   if (this.strict) this.raise(this.start, "'with' in strict mode")
384   this.next()
385   node.object = this.parseParenExpression()
386   node.body = this.parseStatement("with")
387   return this.finishNode(node, "WithStatement")
388 }
389
390 pp.parseEmptyStatement = function(node) {
391   this.next()
392   return this.finishNode(node, "EmptyStatement")
393 }
394
395 pp.parseLabeledStatement = function(node, maybeName, expr, context) {
396   for (let label of this.labels)
397     if (label.name === maybeName)
398       this.raise(expr.start, "Label '" + maybeName + "' is already declared")
399   let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null
400   for (let i = this.labels.length - 1; i >= 0; i--) {
401     let label = this.labels[i]
402     if (label.statementStart === node.start) {
403       // Update information about previous labels on this node
404       label.statementStart = this.start
405       label.kind = kind
406     } else break
407   }
408   this.labels.push({name: maybeName, kind, statementStart: this.start})
409   node.body = this.parseStatement(context ? context.indexOf("label") === -1 ? context + "label" : context : "label")
410   this.labels.pop()
411   node.label = expr
412   return this.finishNode(node, "LabeledStatement")
413 }
414
415 pp.parseExpressionStatement = function(node, expr) {
416   node.expression = expr
417   this.semicolon()
418   return this.finishNode(node, "ExpressionStatement")
419 }
420
421 // Parse a semicolon-enclosed block of statements, handling `"use
422 // strict"` declarations when `allowStrict` is true (used for
423 // function bodies).
424
425 pp.parseBlock = function(createNewLexicalScope = true, node = this.startNode(), exitStrict) {
426   node.body = []
427   this.expect(tt.braceL)
428   if (createNewLexicalScope) this.enterScope(0)
429   while (this.type !== tt.braceR) {
430     let stmt = this.parseStatement(null)
431     node.body.push(stmt)
432   }
433   if (exitStrict) this.strict = false
434   this.next()
435   if (createNewLexicalScope) this.exitScope()
436   return this.finishNode(node, "BlockStatement")
437 }
438
439 // Parse a regular `for` loop. The disambiguation code in
440 // `parseStatement` will already have parsed the init statement or
441 // expression.
442
443 pp.parseFor = function(node, init) {
444   node.init = init
445   this.expect(tt.semi)
446   node.test = this.type === tt.semi ? null : this.parseExpression()
447   this.expect(tt.semi)
448   node.update = this.type === tt.parenR ? null : this.parseExpression()
449   this.expect(tt.parenR)
450   node.body = this.parseStatement("for")
451   this.exitScope()
452   this.labels.pop()
453   return this.finishNode(node, "ForStatement")
454 }
455
456 // Parse a `for`/`in` and `for`/`of` loop, which are almost
457 // same from parser's perspective.
458
459 pp.parseForIn = function(node, init) {
460   const isForIn = this.type === tt._in
461   this.next()
462
463   if (
464     init.type === "VariableDeclaration" &&
465     init.declarations[0].init != null &&
466     (
467       !isForIn ||
468       this.options.ecmaVersion < 8 ||
469       this.strict ||
470       init.kind !== "var" ||
471       init.declarations[0].id.type !== "Identifier"
472     )
473   ) {
474     this.raise(
475       init.start,
476       `${
477         isForIn ? "for-in" : "for-of"
478       } loop variable declaration may not have an initializer`
479     )
480   }
481   node.left = init
482   node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign()
483   this.expect(tt.parenR)
484   node.body = this.parseStatement("for")
485   this.exitScope()
486   this.labels.pop()
487   return this.finishNode(node, isForIn ? "ForInStatement" : "ForOfStatement")
488 }
489
490 // Parse a list of variable declarations.
491
492 pp.parseVar = function(node, isFor, kind) {
493   node.declarations = []
494   node.kind = kind
495   for (;;) {
496     let decl = this.startNode()
497     this.parseVarId(decl, kind)
498     if (this.eat(tt.eq)) {
499       decl.init = this.parseMaybeAssign(isFor)
500     } else if (kind === "const" && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) {
501       this.unexpected()
502     } else if (decl.id.type !== "Identifier" && !(isFor && (this.type === tt._in || this.isContextual("of")))) {
503       this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value")
504     } else {
505       decl.init = null
506     }
507     node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
508     if (!this.eat(tt.comma)) break
509   }
510   return node
511 }
512
513 pp.parseVarId = function(decl, kind) {
514   decl.id = this.parseBindingAtom()
515   this.checkLValPattern(decl.id, kind === "var" ? BIND_VAR : BIND_LEXICAL, false)
516 }
517
518 const FUNC_STATEMENT = 1, FUNC_HANGING_STATEMENT = 2, FUNC_NULLABLE_ID = 4
519
520 // Parse a function declaration or literal (depending on the
521 // `statement & FUNC_STATEMENT`).
522
523 // Remove `allowExpressionBody` for 7.0.0, as it is only called with false
524 pp.parseFunction = function(node, statement, allowExpressionBody, isAsync, forInit) {
525   this.initFunction(node)
526   if (this.options.ecmaVersion >= 9 || this.options.ecmaVersion >= 6 && !isAsync) {
527     if (this.type === tt.star && (statement & FUNC_HANGING_STATEMENT))
528       this.unexpected()
529     node.generator = this.eat(tt.star)
530   }
531   if (this.options.ecmaVersion >= 8)
532     node.async = !!isAsync
533
534   if (statement & FUNC_STATEMENT) {
535     node.id = (statement & FUNC_NULLABLE_ID) && this.type !== tt.name ? null : this.parseIdent()
536     if (node.id && !(statement & FUNC_HANGING_STATEMENT))
537       // If it is a regular function declaration in sloppy mode, then it is
538       // subject to Annex B semantics (BIND_FUNCTION). Otherwise, the binding
539       // mode depends on properties of the current scope (see
540       // treatFunctionsAsVar).
541       this.checkLValSimple(node.id, (this.strict || node.generator || node.async) ? this.treatFunctionsAsVar ? BIND_VAR : BIND_LEXICAL : BIND_FUNCTION)
542   }
543
544   let oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos
545   this.yieldPos = 0
546   this.awaitPos = 0
547   this.awaitIdentPos = 0
548   this.enterScope(functionFlags(node.async, node.generator))
549
550   if (!(statement & FUNC_STATEMENT))
551     node.id = this.type === tt.name ? this.parseIdent() : null
552
553   this.parseFunctionParams(node)
554   this.parseFunctionBody(node, allowExpressionBody, false, forInit)
555
556   this.yieldPos = oldYieldPos
557   this.awaitPos = oldAwaitPos
558   this.awaitIdentPos = oldAwaitIdentPos
559   return this.finishNode(node, (statement & FUNC_STATEMENT) ? "FunctionDeclaration" : "FunctionExpression")
560 }
561
562 pp.parseFunctionParams = function(node) {
563   this.expect(tt.parenL)
564   node.params = this.parseBindingList(tt.parenR, false, this.options.ecmaVersion >= 8)
565   this.checkYieldAwaitInDefaultParams()
566 }
567
568 // Parse a class declaration or literal (depending on the
569 // `isStatement` parameter).
570
571 pp.parseClass = function(node, isStatement) {
572   this.next()
573
574   // ecma-262 14.6 Class Definitions
575   // A class definition is always strict mode code.
576   const oldStrict = this.strict
577   this.strict = true
578
579   this.parseClassId(node, isStatement)
580   this.parseClassSuper(node)
581   const privateNameMap = this.enterClassBody()
582   const classBody = this.startNode()
583   let hadConstructor = false
584   classBody.body = []
585   this.expect(tt.braceL)
586   while (this.type !== tt.braceR) {
587     const element = this.parseClassElement(node.superClass !== null)
588     if (element) {
589       classBody.body.push(element)
590       if (element.type === "MethodDefinition" && element.kind === "constructor") {
591         if (hadConstructor) this.raise(element.start, "Duplicate constructor in the same class")
592         hadConstructor = true
593       } else if (element.key && element.key.type === "PrivateIdentifier" && isPrivateNameConflicted(privateNameMap, element)) {
594         this.raiseRecoverable(element.key.start, `Identifier '#${element.key.name}' has already been declared`)
595       }
596     }
597   }
598   this.strict = oldStrict
599   this.next()
600   node.body = this.finishNode(classBody, "ClassBody")
601   this.exitClassBody()
602   return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
603 }
604
605 pp.parseClassElement = function(constructorAllowsSuper) {
606   if (this.eat(tt.semi)) return null
607
608   const ecmaVersion = this.options.ecmaVersion
609   const node = this.startNode()
610   let keyName = ""
611   let isGenerator = false
612   let isAsync = false
613   let kind = "method"
614   let isStatic = false
615
616   if (this.eatContextual("static")) {
617     // Parse static init block
618     if (ecmaVersion >= 13 && this.eat(tt.braceL)) {
619       this.parseClassStaticBlock(node)
620       return node
621     }
622     if (this.isClassElementNameStart() || this.type === tt.star) {
623       isStatic = true
624     } else {
625       keyName = "static"
626     }
627   }
628   node.static = isStatic
629   if (!keyName && ecmaVersion >= 8 && this.eatContextual("async")) {
630     if ((this.isClassElementNameStart() || this.type === tt.star) && !this.canInsertSemicolon()) {
631       isAsync = true
632     } else {
633       keyName = "async"
634     }
635   }
636   if (!keyName && (ecmaVersion >= 9 || !isAsync) && this.eat(tt.star)) {
637     isGenerator = true
638   }
639   if (!keyName && !isAsync && !isGenerator) {
640     const lastValue = this.value
641     if (this.eatContextual("get") || this.eatContextual("set")) {
642       if (this.isClassElementNameStart()) {
643         kind = lastValue
644       } else {
645         keyName = lastValue
646       }
647     }
648   }
649
650   // Parse element name
651   if (keyName) {
652     // 'async', 'get', 'set', or 'static' were not a keyword contextually.
653     // The last token is any of those. Make it the element name.
654     node.computed = false
655     node.key = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc)
656     node.key.name = keyName
657     this.finishNode(node.key, "Identifier")
658   } else {
659     this.parseClassElementName(node)
660   }
661
662   // Parse element value
663   if (ecmaVersion < 13 || this.type === tt.parenL || kind !== "method" || isGenerator || isAsync) {
664     const isConstructor = !node.static && checkKeyName(node, "constructor")
665     const allowsDirectSuper = isConstructor && constructorAllowsSuper
666     // Couldn't move this check into the 'parseClassMethod' method for backward compatibility.
667     if (isConstructor && kind !== "method") this.raise(node.key.start, "Constructor can't have get/set modifier")
668     node.kind = isConstructor ? "constructor" : kind
669     this.parseClassMethod(node, isGenerator, isAsync, allowsDirectSuper)
670   } else {
671     this.parseClassField(node)
672   }
673
674   return node
675 }
676
677 pp.isClassElementNameStart = function() {
678   return (
679     this.type === tt.name ||
680     this.type === tt.privateId ||
681     this.type === tt.num ||
682     this.type === tt.string ||
683     this.type === tt.bracketL ||
684     this.type.keyword
685   )
686 }
687
688 pp.parseClassElementName = function(element) {
689   if (this.type === tt.privateId) {
690     if (this.value === "constructor") {
691       this.raise(this.start, "Classes can't have an element named '#constructor'")
692     }
693     element.computed = false
694     element.key = this.parsePrivateIdent()
695   } else {
696     this.parsePropertyName(element)
697   }
698 }
699
700 pp.parseClassMethod = function(method, isGenerator, isAsync, allowsDirectSuper) {
701   // Check key and flags
702   const key = method.key
703   if (method.kind === "constructor") {
704     if (isGenerator) this.raise(key.start, "Constructor can't be a generator")
705     if (isAsync) this.raise(key.start, "Constructor can't be an async method")
706   } else if (method.static && checkKeyName(method, "prototype")) {
707     this.raise(key.start, "Classes may not have a static property named prototype")
708   }
709
710   // Parse value
711   const value = method.value = this.parseMethod(isGenerator, isAsync, allowsDirectSuper)
712
713   // Check value
714   if (method.kind === "get" && value.params.length !== 0)
715     this.raiseRecoverable(value.start, "getter should have no params")
716   if (method.kind === "set" && value.params.length !== 1)
717     this.raiseRecoverable(value.start, "setter should have exactly one param")
718   if (method.kind === "set" && value.params[0].type === "RestElement")
719     this.raiseRecoverable(value.params[0].start, "Setter cannot use rest params")
720
721   return this.finishNode(method, "MethodDefinition")
722 }
723
724 pp.parseClassField = function(field) {
725   if (checkKeyName(field, "constructor")) {
726     this.raise(field.key.start, "Classes can't have a field named 'constructor'")
727   } else if (field.static && checkKeyName(field, "prototype")) {
728     this.raise(field.key.start, "Classes can't have a static field named 'prototype'")
729   }
730
731   if (this.eat(tt.eq)) {
732     // To raise SyntaxError if 'arguments' exists in the initializer.
733     const scope = this.currentThisScope()
734     const inClassFieldInit = scope.inClassFieldInit
735     scope.inClassFieldInit = true
736     field.value = this.parseMaybeAssign()
737     scope.inClassFieldInit = inClassFieldInit
738   } else {
739     field.value = null
740   }
741   this.semicolon()
742
743   return this.finishNode(field, "PropertyDefinition")
744 }
745
746 pp.parseClassStaticBlock = function(node) {
747   node.body = []
748
749   let oldLabels = this.labels
750   this.labels = []
751   this.enterScope(SCOPE_CLASS_STATIC_BLOCK | SCOPE_SUPER)
752   while (this.type !== tt.braceR) {
753     let stmt = this.parseStatement(null)
754     node.body.push(stmt)
755   }
756   this.next()
757   this.exitScope()
758   this.labels = oldLabels
759
760   return this.finishNode(node, "StaticBlock")
761 }
762
763 pp.parseClassId = function(node, isStatement) {
764   if (this.type === tt.name) {
765     node.id = this.parseIdent()
766     if (isStatement)
767       this.checkLValSimple(node.id, BIND_LEXICAL, false)
768   } else {
769     if (isStatement === true)
770       this.unexpected()
771     node.id = null
772   }
773 }
774
775 pp.parseClassSuper = function(node) {
776   node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts(false) : null
777 }
778
779 pp.enterClassBody = function() {
780   const element = {declared: Object.create(null), used: []}
781   this.privateNameStack.push(element)
782   return element.declared
783 }
784
785 pp.exitClassBody = function() {
786   const {declared, used} = this.privateNameStack.pop()
787   const len = this.privateNameStack.length
788   const parent = len === 0 ? null : this.privateNameStack[len - 1]
789   for (let i = 0; i < used.length; ++i) {
790     const id = used[i]
791     if (!hasOwn(declared, id.name)) {
792       if (parent) {
793         parent.used.push(id)
794       } else {
795         this.raiseRecoverable(id.start, `Private field '#${id.name}' must be declared in an enclosing class`)
796       }
797     }
798   }
799 }
800
801 function isPrivateNameConflicted(privateNameMap, element) {
802   const name = element.key.name
803   const curr = privateNameMap[name]
804
805   let next = "true"
806   if (element.type === "MethodDefinition" && (element.kind === "get" || element.kind === "set")) {
807     next = (element.static ? "s" : "i") + element.kind
808   }
809
810   // `class { get #a(){}; static set #a(_){} }` is also conflict.
811   if (
812     curr === "iget" && next === "iset" ||
813     curr === "iset" && next === "iget" ||
814     curr === "sget" && next === "sset" ||
815     curr === "sset" && next === "sget"
816   ) {
817     privateNameMap[name] = "true"
818     return false
819   } else if (!curr) {
820     privateNameMap[name] = next
821     return false
822   } else {
823     return true
824   }
825 }
826
827 function checkKeyName(node, name) {
828   const {computed, key} = node
829   return !computed && (
830     key.type === "Identifier" && key.name === name ||
831     key.type === "Literal" && key.value === name
832   )
833 }
834
835 // Parses module export declaration.
836
837 pp.parseExport = function(node, exports) {
838   this.next()
839   // export * from '...'
840   if (this.eat(tt.star)) {
841     if (this.options.ecmaVersion >= 11) {
842       if (this.eatContextual("as")) {
843         node.exported = this.parseModuleExportName()
844         this.checkExport(exports, node.exported, this.lastTokStart)
845       } else {
846         node.exported = null
847       }
848     }
849     this.expectContextual("from")
850     if (this.type !== tt.string) this.unexpected()
851     node.source = this.parseExprAtom()
852     this.semicolon()
853     return this.finishNode(node, "ExportAllDeclaration")
854   }
855   if (this.eat(tt._default)) { // export default ...
856     this.checkExport(exports, "default", this.lastTokStart)
857     let isAsync
858     if (this.type === tt._function || (isAsync = this.isAsyncFunction())) {
859       let fNode = this.startNode()
860       this.next()
861       if (isAsync) this.next()
862       node.declaration = this.parseFunction(fNode, FUNC_STATEMENT | FUNC_NULLABLE_ID, false, isAsync)
863     } else if (this.type === tt._class) {
864       let cNode = this.startNode()
865       node.declaration = this.parseClass(cNode, "nullableID")
866     } else {
867       node.declaration = this.parseMaybeAssign()
868       this.semicolon()
869     }
870     return this.finishNode(node, "ExportDefaultDeclaration")
871   }
872   // export var|const|let|function|class ...
873   if (this.shouldParseExportStatement()) {
874     node.declaration = this.parseStatement(null)
875     if (node.declaration.type === "VariableDeclaration")
876       this.checkVariableExport(exports, node.declaration.declarations)
877     else
878       this.checkExport(exports, node.declaration.id, node.declaration.id.start)
879     node.specifiers = []
880     node.source = null
881   } else { // export { x, y as z } [from '...']
882     node.declaration = null
883     node.specifiers = this.parseExportSpecifiers(exports)
884     if (this.eatContextual("from")) {
885       if (this.type !== tt.string) this.unexpected()
886       node.source = this.parseExprAtom()
887     } else {
888       for (let spec of node.specifiers) {
889         // check for keywords used as local names
890         this.checkUnreserved(spec.local)
891         // check if export is defined
892         this.checkLocalExport(spec.local)
893
894         if (spec.local.type === "Literal") {
895           this.raise(spec.local.start, "A string literal cannot be used as an exported binding without `from`.")
896         }
897       }
898
899       node.source = null
900     }
901     this.semicolon()
902   }
903   return this.finishNode(node, "ExportNamedDeclaration")
904 }
905
906 pp.checkExport = function(exports, name, pos) {
907   if (!exports) return
908   if (typeof name !== "string")
909     name = name.type === "Identifier" ? name.name : name.value
910   if (hasOwn(exports, name))
911     this.raiseRecoverable(pos, "Duplicate export '" + name + "'")
912   exports[name] = true
913 }
914
915 pp.checkPatternExport = function(exports, pat) {
916   let type = pat.type
917   if (type === "Identifier")
918     this.checkExport(exports, pat, pat.start)
919   else if (type === "ObjectPattern")
920     for (let prop of pat.properties)
921       this.checkPatternExport(exports, prop)
922   else if (type === "ArrayPattern")
923     for (let elt of pat.elements) {
924       if (elt) this.checkPatternExport(exports, elt)
925     }
926   else if (type === "Property")
927     this.checkPatternExport(exports, pat.value)
928   else if (type === "AssignmentPattern")
929     this.checkPatternExport(exports, pat.left)
930   else if (type === "RestElement")
931     this.checkPatternExport(exports, pat.argument)
932   else if (type === "ParenthesizedExpression")
933     this.checkPatternExport(exports, pat.expression)
934 }
935
936 pp.checkVariableExport = function(exports, decls) {
937   if (!exports) return
938   for (let decl of decls)
939     this.checkPatternExport(exports, decl.id)
940 }
941
942 pp.shouldParseExportStatement = function() {
943   return this.type.keyword === "var" ||
944     this.type.keyword === "const" ||
945     this.type.keyword === "class" ||
946     this.type.keyword === "function" ||
947     this.isLet() ||
948     this.isAsyncFunction()
949 }
950
951 // Parses a comma-separated list of module exports.
952
953 pp.parseExportSpecifiers = function(exports) {
954   let nodes = [], first = true
955   // export { x, y as z } [from '...']
956   this.expect(tt.braceL)
957   while (!this.eat(tt.braceR)) {
958     if (!first) {
959       this.expect(tt.comma)
960       if (this.afterTrailingComma(tt.braceR)) break
961     } else first = false
962
963     let node = this.startNode()
964     node.local = this.parseModuleExportName()
965     node.exported = this.eatContextual("as") ? this.parseModuleExportName() : node.local
966     this.checkExport(
967       exports,
968       node.exported,
969       node.exported.start
970     )
971     nodes.push(this.finishNode(node, "ExportSpecifier"))
972   }
973   return nodes
974 }
975
976 // Parses import declaration.
977
978 pp.parseImport = function(node) {
979   this.next()
980   // import '...'
981   if (this.type === tt.string) {
982     node.specifiers = empty
983     node.source = this.parseExprAtom()
984   } else {
985     node.specifiers = this.parseImportSpecifiers()
986     this.expectContextual("from")
987     node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
988   }
989   this.semicolon()
990   return this.finishNode(node, "ImportDeclaration")
991 }
992
993 // Parses a comma-separated list of module imports.
994
995 pp.parseImportSpecifiers = function() {
996   let nodes = [], first = true
997   if (this.type === tt.name) {
998     // import defaultObj, { x, y as z } from '...'
999     let node = this.startNode()
1000     node.local = this.parseIdent()
1001     this.checkLValSimple(node.local, BIND_LEXICAL)
1002     nodes.push(this.finishNode(node, "ImportDefaultSpecifier"))
1003     if (!this.eat(tt.comma)) return nodes
1004   }
1005   if (this.type === tt.star) {
1006     let node = this.startNode()
1007     this.next()
1008     this.expectContextual("as")
1009     node.local = this.parseIdent()
1010     this.checkLValSimple(node.local, BIND_LEXICAL)
1011     nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"))
1012     return nodes
1013   }
1014   this.expect(tt.braceL)
1015   while (!this.eat(tt.braceR)) {
1016     if (!first) {
1017       this.expect(tt.comma)
1018       if (this.afterTrailingComma(tt.braceR)) break
1019     } else first = false
1020
1021     let node = this.startNode()
1022     node.imported = this.parseModuleExportName()
1023     if (this.eatContextual("as")) {
1024       node.local = this.parseIdent()
1025     } else {
1026       this.checkUnreserved(node.imported)
1027       node.local = node.imported
1028     }
1029     this.checkLValSimple(node.local, BIND_LEXICAL)
1030     nodes.push(this.finishNode(node, "ImportSpecifier"))
1031   }
1032   return nodes
1033 }
1034
1035 pp.parseModuleExportName = function() {
1036   if (this.options.ecmaVersion >= 13 && this.type === tt.string) {
1037     const stringLiteral = this.parseLiteral(this.value)
1038     if (loneSurrogate.test(stringLiteral.value)) {
1039       this.raise(stringLiteral.start, "An export name cannot include a lone surrogate.")
1040     }
1041     return stringLiteral
1042   }
1043   return this.parseIdent(true)
1044 }
1045
1046 // Set `ExpressionStatement#directive` property for directive prologues.
1047 pp.adaptDirectivePrologue = function(statements) {
1048   for (let i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {
1049     statements[i].directive = statements[i].expression.raw.slice(1, -1)
1050   }
1051 }
1052 pp.isDirectiveCandidate = function(statement) {
1053   return (
1054     statement.type === "ExpressionStatement" &&
1055     statement.expression.type === "Literal" &&
1056     typeof statement.expression.value === "string" &&
1057     // Reject parenthesized strings.
1058     (this.input[statement.start] === "\"" || this.input[statement.start] === "'")
1059   )
1060 }