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"
9 const pp = Parser.prototype
11 // ### Statement parsing
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.
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)
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)
30 node.sourceType = this.options.sourceType
31 return this.finishNode(node, "Program")
34 const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}
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
48 if (nextCh === 123) return true // '{'
49 if (isIdentifierStart(nextCh, true)) {
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
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"))
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))
75 // Parse a single statement.
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
82 pp.parseStatement = function(context, topLevel, exports) {
83 let starttype = this.type, node = this.startNode(), kind
85 if (this.isLet(context)) {
90 // Most types of statements are recognized by the keyword they
91 // start with. Many are trivial to parse, some require a bit of
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)
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)
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)
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())
131 if (!this.options.allowImportExportEverywhere) {
133 this.raise(this.start, "'import' and 'export' may only appear at the top level")
135 this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'")
137 return starttype === tt._import ? this.parseImport(node) : this.parseExport(node, exports)
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.
145 if (this.isAsyncFunction()) {
146 if (context) this.unexpected()
148 return this.parseFunctionStatement(node, true, !context)
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)
158 pp.parseBreakContinueStatement = function(node, keyword) {
159 let isBreak = keyword === "break"
161 if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null
162 else if (this.type !== tt.name) this.unexpected()
164 node.label = this.parseIdent()
168 // Verify that there is an actual destination to break or
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
178 if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword)
179 return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
182 pp.parseDebuggerStatement = function(node) {
185 return this.finishNode(node, "DebuggerStatement")
188 pp.parseDoStatement = function(node) {
190 this.labels.push(loopLabel)
191 node.body = this.parseStatement("do")
193 this.expect(tt._while)
194 node.test = this.parseParenExpression()
195 if (this.options.ecmaVersion >= 6)
199 return this.finishNode(node, "DoWhileStatement")
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.
210 pp.parseForStatement = function(node) {
212 let awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual("await")) ? this.lastTokStart : -1
213 this.labels.push(loopLabel)
215 this.expect(tt.parenL)
216 if (this.type === tt.semi) {
217 if (awaitAt > -1) this.unexpected(awaitAt)
218 return this.parseFor(node, null)
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
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
232 return this.parseForIn(node, init)
234 if (awaitAt > -1) this.unexpected(awaitAt)
235 return this.parseFor(node, init)
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
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)
251 this.checkExpressionErrors(refDestructuringErrors, true)
253 if (awaitAt > -1) this.unexpected(awaitAt)
254 return this.parseFor(node, init)
257 pp.parseFunctionStatement = function(node, isAsync, declarationPosition) {
259 return this.parseFunction(node, FUNC_STATEMENT | (declarationPosition ? 0 : FUNC_HANGING_STATEMENT), false, isAsync)
262 pp.parseIfStatement = function(node) {
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")
271 pp.parseReturnStatement = function(node) {
272 if (!this.inFunction && !this.options.allowReturnOutsideFunction)
273 this.raise(this.start, "'return' outside of function")
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.
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")
285 pp.parseSwitchStatement = function(node) {
287 node.discriminant = this.parseParenExpression()
289 this.expect(tt.braceL)
290 this.labels.push(switchLabel)
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.
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())
306 cur.test = this.parseExpression()
308 if (sawDefault) this.raiseRecoverable(this.lastTokStart, "Multiple default clauses")
312 this.expect(tt.colon)
314 if (!cur) this.unexpected()
315 cur.consequent.push(this.parseStatement(null))
319 if (cur) this.finishNode(cur, "SwitchCase")
320 this.next() // Closing brace
322 return this.finishNode(node, "SwitchStatement")
325 pp.parseThrowStatement = function(node) {
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()
331 return this.finishNode(node, "ThrowStatement")
334 // Reused empty array added for node fields that are always empty.
338 pp.parseTryStatement = function(node) {
340 node.block = this.parseBlock()
342 if (this.type === tt._catch) {
343 let clause = this.startNode()
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)
352 if (this.options.ecmaVersion < 10) this.unexpected()
356 clause.body = this.parseBlock(false)
358 node.handler = this.finishNode(clause, "CatchClause")
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")
366 pp.parseVarStatement = function(node, kind) {
368 this.parseVar(node, false, kind)
370 return this.finishNode(node, "VariableDeclaration")
373 pp.parseWhileStatement = function(node) {
375 node.test = this.parseParenExpression()
376 this.labels.push(loopLabel)
377 node.body = this.parseStatement("while")
379 return this.finishNode(node, "WhileStatement")
382 pp.parseWithStatement = function(node) {
383 if (this.strict) this.raise(this.start, "'with' in strict mode")
385 node.object = this.parseParenExpression()
386 node.body = this.parseStatement("with")
387 return this.finishNode(node, "WithStatement")
390 pp.parseEmptyStatement = function(node) {
392 return this.finishNode(node, "EmptyStatement")
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
408 this.labels.push({name: maybeName, kind, statementStart: this.start})
409 node.body = this.parseStatement(context ? context.indexOf("label") === -1 ? context + "label" : context : "label")
412 return this.finishNode(node, "LabeledStatement")
415 pp.parseExpressionStatement = function(node, expr) {
416 node.expression = expr
418 return this.finishNode(node, "ExpressionStatement")
421 // Parse a semicolon-enclosed block of statements, handling `"use
422 // strict"` declarations when `allowStrict` is true (used for
425 pp.parseBlock = function(createNewLexicalScope = true, node = this.startNode(), exitStrict) {
427 this.expect(tt.braceL)
428 if (createNewLexicalScope) this.enterScope(0)
429 while (this.type !== tt.braceR) {
430 let stmt = this.parseStatement(null)
433 if (exitStrict) this.strict = false
435 if (createNewLexicalScope) this.exitScope()
436 return this.finishNode(node, "BlockStatement")
439 // Parse a regular `for` loop. The disambiguation code in
440 // `parseStatement` will already have parsed the init statement or
443 pp.parseFor = function(node, init) {
446 node.test = this.type === tt.semi ? null : this.parseExpression()
448 node.update = this.type === tt.parenR ? null : this.parseExpression()
449 this.expect(tt.parenR)
450 node.body = this.parseStatement("for")
453 return this.finishNode(node, "ForStatement")
456 // Parse a `for`/`in` and `for`/`of` loop, which are almost
457 // same from parser's perspective.
459 pp.parseForIn = function(node, init) {
460 const isForIn = this.type === tt._in
464 init.type === "VariableDeclaration" &&
465 init.declarations[0].init != null &&
468 this.options.ecmaVersion < 8 ||
470 init.kind !== "var" ||
471 init.declarations[0].id.type !== "Identifier"
477 isForIn ? "for-in" : "for-of"
478 } loop variable declaration may not have an initializer`
482 node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign()
483 this.expect(tt.parenR)
484 node.body = this.parseStatement("for")
487 return this.finishNode(node, isForIn ? "ForInStatement" : "ForOfStatement")
490 // Parse a list of variable declarations.
492 pp.parseVar = function(node, isFor, kind) {
493 node.declarations = []
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")))) {
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")
507 node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
508 if (!this.eat(tt.comma)) break
513 pp.parseVarId = function(decl, kind) {
514 decl.id = this.parseBindingAtom()
515 this.checkLValPattern(decl.id, kind === "var" ? BIND_VAR : BIND_LEXICAL, false)
518 const FUNC_STATEMENT = 1, FUNC_HANGING_STATEMENT = 2, FUNC_NULLABLE_ID = 4
520 // Parse a function declaration or literal (depending on the
521 // `statement & FUNC_STATEMENT`).
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))
529 node.generator = this.eat(tt.star)
531 if (this.options.ecmaVersion >= 8)
532 node.async = !!isAsync
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)
544 let oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos
547 this.awaitIdentPos = 0
548 this.enterScope(functionFlags(node.async, node.generator))
550 if (!(statement & FUNC_STATEMENT))
551 node.id = this.type === tt.name ? this.parseIdent() : null
553 this.parseFunctionParams(node)
554 this.parseFunctionBody(node, allowExpressionBody, false, forInit)
556 this.yieldPos = oldYieldPos
557 this.awaitPos = oldAwaitPos
558 this.awaitIdentPos = oldAwaitIdentPos
559 return this.finishNode(node, (statement & FUNC_STATEMENT) ? "FunctionDeclaration" : "FunctionExpression")
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()
568 // Parse a class declaration or literal (depending on the
569 // `isStatement` parameter).
571 pp.parseClass = function(node, isStatement) {
574 // ecma-262 14.6 Class Definitions
575 // A class definition is always strict mode code.
576 const oldStrict = this.strict
579 this.parseClassId(node, isStatement)
580 this.parseClassSuper(node)
581 const privateNameMap = this.enterClassBody()
582 const classBody = this.startNode()
583 let hadConstructor = false
585 this.expect(tt.braceL)
586 while (this.type !== tt.braceR) {
587 const element = this.parseClassElement(node.superClass !== null)
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`)
598 this.strict = oldStrict
600 node.body = this.finishNode(classBody, "ClassBody")
602 return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
605 pp.parseClassElement = function(constructorAllowsSuper) {
606 if (this.eat(tt.semi)) return null
608 const ecmaVersion = this.options.ecmaVersion
609 const node = this.startNode()
611 let isGenerator = false
616 if (this.eatContextual("static")) {
617 // Parse static init block
618 if (ecmaVersion >= 13 && this.eat(tt.braceL)) {
619 this.parseClassStaticBlock(node)
622 if (this.isClassElementNameStart() || this.type === tt.star) {
628 node.static = isStatic
629 if (!keyName && ecmaVersion >= 8 && this.eatContextual("async")) {
630 if ((this.isClassElementNameStart() || this.type === tt.star) && !this.canInsertSemicolon()) {
636 if (!keyName && (ecmaVersion >= 9 || !isAsync) && this.eat(tt.star)) {
639 if (!keyName && !isAsync && !isGenerator) {
640 const lastValue = this.value
641 if (this.eatContextual("get") || this.eatContextual("set")) {
642 if (this.isClassElementNameStart()) {
650 // Parse element name
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")
659 this.parseClassElementName(node)
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)
671 this.parseClassField(node)
677 pp.isClassElementNameStart = function() {
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 ||
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'")
693 element.computed = false
694 element.key = this.parsePrivateIdent()
696 this.parsePropertyName(element)
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")
711 const value = method.value = this.parseMethod(isGenerator, isAsync, allowsDirectSuper)
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")
721 return this.finishNode(method, "MethodDefinition")
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'")
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
743 return this.finishNode(field, "PropertyDefinition")
746 pp.parseClassStaticBlock = function(node) {
749 let oldLabels = this.labels
751 this.enterScope(SCOPE_CLASS_STATIC_BLOCK | SCOPE_SUPER)
752 while (this.type !== tt.braceR) {
753 let stmt = this.parseStatement(null)
758 this.labels = oldLabels
760 return this.finishNode(node, "StaticBlock")
763 pp.parseClassId = function(node, isStatement) {
764 if (this.type === tt.name) {
765 node.id = this.parseIdent()
767 this.checkLValSimple(node.id, BIND_LEXICAL, false)
769 if (isStatement === true)
775 pp.parseClassSuper = function(node) {
776 node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts(false) : null
779 pp.enterClassBody = function() {
780 const element = {declared: Object.create(null), used: []}
781 this.privateNameStack.push(element)
782 return element.declared
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) {
791 if (!hasOwn(declared, id.name)) {
795 this.raiseRecoverable(id.start, `Private field '#${id.name}' must be declared in an enclosing class`)
801 function isPrivateNameConflicted(privateNameMap, element) {
802 const name = element.key.name
803 const curr = privateNameMap[name]
806 if (element.type === "MethodDefinition" && (element.kind === "get" || element.kind === "set")) {
807 next = (element.static ? "s" : "i") + element.kind
810 // `class { get #a(){}; static set #a(_){} }` is also conflict.
812 curr === "iget" && next === "iset" ||
813 curr === "iset" && next === "iget" ||
814 curr === "sget" && next === "sset" ||
815 curr === "sset" && next === "sget"
817 privateNameMap[name] = "true"
820 privateNameMap[name] = next
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
835 // Parses module export declaration.
837 pp.parseExport = function(node, exports) {
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)
849 this.expectContextual("from")
850 if (this.type !== tt.string) this.unexpected()
851 node.source = this.parseExprAtom()
853 return this.finishNode(node, "ExportAllDeclaration")
855 if (this.eat(tt._default)) { // export default ...
856 this.checkExport(exports, "default", this.lastTokStart)
858 if (this.type === tt._function || (isAsync = this.isAsyncFunction())) {
859 let fNode = this.startNode()
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")
867 node.declaration = this.parseMaybeAssign()
870 return this.finishNode(node, "ExportDefaultDeclaration")
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)
878 this.checkExport(exports, node.declaration.id, node.declaration.id.start)
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()
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)
894 if (spec.local.type === "Literal") {
895 this.raise(spec.local.start, "A string literal cannot be used as an exported binding without `from`.")
903 return this.finishNode(node, "ExportNamedDeclaration")
906 pp.checkExport = function(exports, name, pos) {
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 + "'")
915 pp.checkPatternExport = function(exports, pat) {
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)
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)
936 pp.checkVariableExport = function(exports, decls) {
938 for (let decl of decls)
939 this.checkPatternExport(exports, decl.id)
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" ||
948 this.isAsyncFunction()
951 // Parses a comma-separated list of module exports.
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)) {
959 this.expect(tt.comma)
960 if (this.afterTrailingComma(tt.braceR)) break
963 let node = this.startNode()
964 node.local = this.parseModuleExportName()
965 node.exported = this.eatContextual("as") ? this.parseModuleExportName() : node.local
971 nodes.push(this.finishNode(node, "ExportSpecifier"))
976 // Parses import declaration.
978 pp.parseImport = function(node) {
981 if (this.type === tt.string) {
982 node.specifiers = empty
983 node.source = this.parseExprAtom()
985 node.specifiers = this.parseImportSpecifiers()
986 this.expectContextual("from")
987 node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
990 return this.finishNode(node, "ImportDeclaration")
993 // Parses a comma-separated list of module imports.
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
1005 if (this.type === tt.star) {
1006 let node = this.startNode()
1008 this.expectContextual("as")
1009 node.local = this.parseIdent()
1010 this.checkLValSimple(node.local, BIND_LEXICAL)
1011 nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"))
1014 this.expect(tt.braceL)
1015 while (!this.eat(tt.braceR)) {
1017 this.expect(tt.comma)
1018 if (this.afterTrailingComma(tt.braceR)) break
1019 } else first = false
1021 let node = this.startNode()
1022 node.imported = this.parseModuleExportName()
1023 if (this.eatContextual("as")) {
1024 node.local = this.parseIdent()
1026 this.checkUnreserved(node.imported)
1027 node.local = node.imported
1029 this.checkLValSimple(node.local, BIND_LEXICAL)
1030 nodes.push(this.finishNode(node, "ImportSpecifier"))
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.")
1041 return stringLiteral
1043 return this.parseIdent(true)
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)
1052 pp.isDirectiveCandidate = function(statement) {
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] === "'")