Upgrade to https://github.com/acornjs/acorn.git commit 84eda6bf
[jst.git] / src / statement.js
index f5e65ef..3d11d18 100644 (file)
@@ -1,10 +1,10 @@
-import {types as tt} from "./tokentype"
-import {Parser} from "./state"
-import {lineBreak, skipWhiteSpace} from "./whitespace"
-import {isIdentifierStart, isIdentifierChar, keywordRelationalOperator} from "./identifier"
-import {has} from "./util"
-import {DestructuringErrors} from "./parseutil"
-import {functionFlags, SCOPE_SIMPLE_CATCH, BIND_SIMPLE_CATCH, BIND_LEXICAL, BIND_VAR, BIND_FUNCTION} from "./scopeflags"
+import {types as tt} from "./tokentype.js"
+import {Parser} from "./state.js"
+import {lineBreak, skipWhiteSpace} from "./whitespace.js"
+import {isIdentifierStart, isIdentifierChar, keywordRelationalOperator} from "./identifier.js"
+import {hasOwn, loneSurrogate} from "./util.js"
+import {DestructuringErrors} from "./parseutil.js"
+import {functionFlags, SCOPE_SIMPLE_CATCH, BIND_SIMPLE_CATCH, BIND_LEXICAL, BIND_VAR, BIND_FUNCTION, SCOPE_CLASS_STATIC_BLOCK, SCOPE_SUPER} from "./scopeflags.js"
 
 const pp = Parser.prototype
 
@@ -16,31 +16,40 @@ const pp = Parser.prototype
 // to its body instead of creating a new node.
 
 pp.parseTopLevel = function(node) {
-  let exports = {}
+  let exports = Object.create(null)
   if (!node.body) node.body = []
   while (this.type !== tt.eof) {
     let stmt = this.parseStatement(null, true, exports)
     node.body.push(stmt)
   }
+  if (this.inModule)
+    for (let name of Object.keys(this.undefinedExports))
+      this.raiseRecoverable(this.undefinedExports[name].start, `Export '${name}' is not defined`)
   this.adaptDirectivePrologue(node.body)
   this.next()
-  if (this.options.ecmaVersion >= 6) {
-    node.sourceType = this.options.sourceType
-  }
+  node.sourceType = this.options.sourceType
   return this.finishNode(node, "Program")
 }
 
 const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}
 
-pp.isLet = function() {
+pp.isLet = function(context) {
   if (this.options.ecmaVersion < 6 || !this.isContextual("let")) return false
   skipWhiteSpace.lastIndex = this.pos
   let skip = skipWhiteSpace.exec(this.input)
   let next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next)
-  if (nextCh === 91 || nextCh === 123) return true // '{' and '['
+  // For ambiguous cases, determine if a LexicalDeclaration (or only a
+  // Statement) is allowed here. If context is not empty then only a Statement
+  // is allowed. However, `let [` is an explicit negative lookahead for
+  // ExpressionStatement, so special-case it first.
+  if (nextCh === 91 || nextCh === 92 || nextCh > 0xd7ff && nextCh < 0xdc00) return true // '[', '/', astral
+  if (context) return false
+
+  if (nextCh === 123) return true // '{'
   if (isIdentifierStart(nextCh, true)) {
     let pos = next + 1
-    while (isIdentifierChar(this.input.charCodeAt(pos), true)) ++pos
+    while (isIdentifierChar(nextCh = this.input.charCodeAt(pos), true)) ++pos
+    if (nextCh === 92 || nextCh > 0xd7ff && nextCh < 0xdc00) return true
     let ident = this.input.slice(next, pos)
     if (!keywordRelationalOperator.test(ident)) return true
   }
@@ -56,10 +65,11 @@ pp.isAsyncFunction = function() {
 
   skipWhiteSpace.lastIndex = this.pos
   let skip = skipWhiteSpace.exec(this.input)
-  let next = this.pos + skip[0].length
+  let next = this.pos + skip[0].length, after
   return !lineBreak.test(this.input.slice(this.pos, next)) &&
     this.input.slice(next, next + 8) === "function" &&
-    (next + 8 === this.input.length || !isIdentifierChar(this.input.charAt(next + 8)))
+    (next + 8 === this.input.length ||
+     !(isIdentifierChar(after = this.input.charCodeAt(next + 8)) || after > 0xd7ff && after < 0xdc00))
 }
 
 // Parse a single statement.
@@ -72,7 +82,7 @@ pp.isAsyncFunction = function() {
 pp.parseStatement = function(context, topLevel, exports) {
   let starttype = this.type, node = this.startNode(), kind
 
-  if (this.isLet()) {
+  if (this.isLet(context)) {
     starttype = tt._var
     kind = "let"
   }
@@ -87,7 +97,10 @@ pp.parseStatement = function(context, topLevel, exports) {
   case tt._do: return this.parseDoStatement(node)
   case tt._for: return this.parseForStatement(node)
   case tt._function:
-    if ((context && (this.strict || context !== "if")) && this.options.ecmaVersion >= 6) this.unexpected()
+    // Function as sole body of either an if statement or a labeled statement
+    // works, but not when it is part of a labeled statement that is the sole
+    // body of an if statement.
+    if ((context && (this.strict || context !== "if" && context !== "label")) && this.options.ecmaVersion >= 6) this.unexpected()
     return this.parseFunctionStatement(node, false, !context)
   case tt._class:
     if (context) this.unexpected()
@@ -107,6 +120,14 @@ pp.parseStatement = function(context, topLevel, exports) {
   case tt.semi: return this.parseEmptyStatement(node)
   case tt._export:
   case tt._import:
+    if (this.options.ecmaVersion > 10 && starttype === tt._import) {
+      skipWhiteSpace.lastIndex = this.pos
+      let skip = skipWhiteSpace.exec(this.input)
+      let next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next)
+      if (nextCh === 40 || nextCh === 46) // '(' or '.'
+        return this.parseExpressionStatement(node, this.parseExpression())
+    }
+
     if (!this.options.allowImportExportEverywhere) {
       if (!topLevel)
         this.raise(this.start, "'import' and 'export' may only appear at the top level")
@@ -188,7 +209,7 @@ pp.parseDoStatement = function(node) {
 
 pp.parseForStatement = function(node) {
   this.next()
-  let awaitAt = (this.options.ecmaVersion >= 9 && (this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction)) && this.eatContextual("await")) ? this.lastTokStart : -1
+  let awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual("await")) ? this.lastTokStart : -1
   this.labels.push(loopLabel)
   this.enterScope(0)
   this.expect(tt.parenL)
@@ -202,8 +223,7 @@ pp.parseForStatement = function(node) {
     this.next()
     this.parseVar(init, true, kind)
     this.finishNode(init, "VariableDeclaration")
-    if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1 &&
-        !(kind !== "var" && init.declarations[0].init)) {
+    if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1) {
       if (this.options.ecmaVersion >= 9) {
         if (this.type === tt._in) {
           if (awaitAt > -1) this.unexpected(awaitAt)
@@ -214,16 +234,18 @@ pp.parseForStatement = function(node) {
     if (awaitAt > -1) this.unexpected(awaitAt)
     return this.parseFor(node, init)
   }
+  let startsWithLet = this.isContextual("let"), isForOf = false
   let refDestructuringErrors = new DestructuringErrors
-  let init = this.parseExpression(true, refDestructuringErrors)
-  if (this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
+  let init = this.parseExpression(awaitAt > -1 ? "await" : true, refDestructuringErrors)
+  if (this.type === tt._in || (isForOf = this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
     if (this.options.ecmaVersion >= 9) {
       if (this.type === tt._in) {
         if (awaitAt > -1) this.unexpected(awaitAt)
       } else node.await = awaitAt > -1
     }
+    if (startsWithLet && isForOf) this.raise(init.start, "The left-hand side of a for-of loop may not start with 'let'.")
     this.toAssignable(init, false, refDestructuringErrors)
-    this.checkLVal(init)
+    this.checkLValPattern(init)
     return this.parseForIn(node, init)
   } else {
     this.checkExpressionErrors(refDestructuringErrors, true)
@@ -324,7 +346,7 @@ pp.parseTryStatement = function(node) {
       clause.param = this.parseBindingAtom()
       let simple = clause.param.type === "Identifier"
       this.enterScope(simple ? SCOPE_SIMPLE_CATCH : 0)
-      this.checkLVal(clause.param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL)
+      this.checkLValPattern(clause.param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL)
       this.expect(tt.parenR)
     } else {
       if (this.options.ecmaVersion < 10) this.unexpected()
@@ -384,11 +406,7 @@ pp.parseLabeledStatement = function(node, maybeName, expr, context) {
     } else break
   }
   this.labels.push({name: maybeName, kind, statementStart: this.start})
-  node.body = this.parseStatement(context)
-  if (node.body.type === "ClassDeclaration" ||
-      node.body.type === "VariableDeclaration" && node.body.kind !== "var" ||
-      node.body.type === "FunctionDeclaration" && (this.strict || node.body.generator || node.body.async))
-    this.raiseRecoverable(node.body.start, "Invalid labeled declaration")
+  node.body = this.parseStatement(context ? context.indexOf("label") === -1 ? context + "label" : context : "label")
   this.labels.pop()
   node.label = expr
   return this.finishNode(node, "LabeledStatement")
@@ -404,14 +422,16 @@ pp.parseExpressionStatement = function(node, expr) {
 // strict"` declarations when `allowStrict` is true (used for
 // function bodies).
 
-pp.parseBlock = function(createNewLexicalScope = true, node = this.startNode()) {
+pp.parseBlock = function(createNewLexicalScope = true, node = this.startNode(), exitStrict) {
   node.body = []
   this.expect(tt.braceL)
   if (createNewLexicalScope) this.enterScope(0)
-  while (!this.eat(tt.braceR)) {
+  while (this.type !== tt.braceR) {
     let stmt = this.parseStatement(null)
     node.body.push(stmt)
   }
+  if (exitStrict) this.strict = false
+  this.next()
   if (createNewLexicalScope) this.exitScope()
   return this.finishNode(node, "BlockStatement")
 }
@@ -427,8 +447,8 @@ pp.parseFor = function(node, init) {
   this.expect(tt.semi)
   node.update = this.type === tt.parenR ? null : this.parseExpression()
   this.expect(tt.parenR)
-  this.exitScope()
   node.body = this.parseStatement("for")
+  this.exitScope()
   this.labels.pop()
   return this.finishNode(node, "ForStatement")
 }
@@ -437,21 +457,34 @@ pp.parseFor = function(node, init) {
 // same from parser's perspective.
 
 pp.parseForIn = function(node, init) {
-  let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement"
+  const isForIn = this.type === tt._in
   this.next()
-  if (type === "ForInStatement") {
-    if (init.type === "AssignmentPattern" ||
-      (init.type === "VariableDeclaration" && init.declarations[0].init != null &&
-       (this.strict || init.declarations[0].id.type !== "Identifier")))
-      this.raise(init.start, "Invalid assignment in for-in loop head")
+
+  if (
+    init.type === "VariableDeclaration" &&
+    init.declarations[0].init != null &&
+    (
+      !isForIn ||
+      this.options.ecmaVersion < 8 ||
+      this.strict ||
+      init.kind !== "var" ||
+      init.declarations[0].id.type !== "Identifier"
+    )
+  ) {
+    this.raise(
+      init.start,
+      `${
+        isForIn ? "for-in" : "for-of"
+      } loop variable declaration may not have an initializer`
+    )
   }
   node.left = init
-  node.right = type === "ForInStatement" ? this.parseExpression() : this.parseMaybeAssign()
+  node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign()
   this.expect(tt.parenR)
-  this.exitScope()
   node.body = this.parseStatement("for")
+  this.exitScope()
   this.labels.pop()
-  return this.finishNode(node, type)
+  return this.finishNode(node, isForIn ? "ForInStatement" : "ForOfStatement")
 }
 
 // Parse a list of variable declarations.
@@ -478,41 +511,51 @@ pp.parseVar = function(node, isFor, kind) {
 }
 
 pp.parseVarId = function(decl, kind) {
-  decl.id = this.parseBindingAtom(kind)
-  this.checkLVal(decl.id, kind === "var" ? BIND_VAR : BIND_LEXICAL, false)
+  decl.id = this.parseBindingAtom()
+  this.checkLValPattern(decl.id, kind === "var" ? BIND_VAR : BIND_LEXICAL, false)
 }
 
 const FUNC_STATEMENT = 1, FUNC_HANGING_STATEMENT = 2, FUNC_NULLABLE_ID = 4
 
 // Parse a function declaration or literal (depending on the
-// `isStatement` parameter).
+// `statement & FUNC_STATEMENT`).
 
-pp.parseFunction = function(node, statement, allowExpressionBody, isAsync) {
+// Remove `allowExpressionBody` for 7.0.0, as it is only called with false
+pp.parseFunction = function(node, statement, allowExpressionBody, isAsync, forInit) {
   this.initFunction(node)
-  if (this.options.ecmaVersion >= 9 || this.options.ecmaVersion >= 6 && !isAsync)
+  if (this.options.ecmaVersion >= 9 || this.options.ecmaVersion >= 6 && !isAsync) {
+    if (this.type === tt.star && (statement & FUNC_HANGING_STATEMENT))
+      this.unexpected()
     node.generator = this.eat(tt.star)
+  }
   if (this.options.ecmaVersion >= 8)
     node.async = !!isAsync
 
   if (statement & FUNC_STATEMENT) {
     node.id = (statement & FUNC_NULLABLE_ID) && this.type !== tt.name ? null : this.parseIdent()
     if (node.id && !(statement & FUNC_HANGING_STATEMENT))
-      this.checkLVal(node.id, this.inModule && !this.inFunction ? BIND_LEXICAL : BIND_FUNCTION)
+      // If it is a regular function declaration in sloppy mode, then it is
+      // subject to Annex B semantics (BIND_FUNCTION). Otherwise, the binding
+      // mode depends on properties of the current scope (see
+      // treatFunctionsAsVar).
+      this.checkLValSimple(node.id, (this.strict || node.generator || node.async) ? this.treatFunctionsAsVar ? BIND_VAR : BIND_LEXICAL : BIND_FUNCTION)
   }
 
-  let oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos
+  let oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos
   this.yieldPos = 0
   this.awaitPos = 0
+  this.awaitIdentPos = 0
   this.enterScope(functionFlags(node.async, node.generator))
 
   if (!(statement & FUNC_STATEMENT))
     node.id = this.type === tt.name ? this.parseIdent() : null
 
   this.parseFunctionParams(node)
-  this.parseFunctionBody(node, allowExpressionBody)
+  this.parseFunctionBody(node, allowExpressionBody, false, forInit)
 
   this.yieldPos = oldYieldPos
   this.awaitPos = oldAwaitPos
+  this.awaitIdentPos = oldAwaitIdentPos
   return this.finishNode(node, (statement & FUNC_STATEMENT) ? "FunctionDeclaration" : "FunctionExpression")
 }
 
@@ -528,90 +571,265 @@ pp.parseFunctionParams = function(node) {
 pp.parseClass = function(node, isStatement) {
   this.next()
 
+  // ecma-262 14.6 Class Definitions
+  // A class definition is always strict mode code.
+  const oldStrict = this.strict
+  this.strict = true
+
   this.parseClassId(node, isStatement)
   this.parseClassSuper(node)
-  let classBody = this.startNode()
+  const privateNameMap = this.enterClassBody()
+  const classBody = this.startNode()
   let hadConstructor = false
   classBody.body = []
   this.expect(tt.braceL)
-  while (!this.eat(tt.braceR)) {
+  while (this.type !== tt.braceR) {
     const element = this.parseClassElement(node.superClass !== null)
     if (element) {
       classBody.body.push(element)
       if (element.type === "MethodDefinition" && element.kind === "constructor") {
         if (hadConstructor) this.raise(element.start, "Duplicate constructor in the same class")
         hadConstructor = true
+      } else if (element.key && element.key.type === "PrivateIdentifier" && isPrivateNameConflicted(privateNameMap, element)) {
+        this.raiseRecoverable(element.key.start, `Identifier '#${element.key.name}' has already been declared`)
       }
     }
   }
+  this.strict = oldStrict
+  this.next()
   node.body = this.finishNode(classBody, "ClassBody")
+  this.exitClassBody()
   return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
 }
 
 pp.parseClassElement = function(constructorAllowsSuper) {
   if (this.eat(tt.semi)) return null
 
-  let method = this.startNode()
-  const tryContextual = (k, noLineBreak = false) => {
-    const start = this.start, startLoc = this.startLoc
-    if (!this.eatContextual(k)) return false
-    if (this.type !== tt.parenL && (!noLineBreak || !this.canInsertSemicolon())) return true
-    if (method.key) this.unexpected()
-    method.computed = false
-    method.key = this.startNodeAt(start, startLoc)
-    method.key.name = k
-    this.finishNode(method.key, "Identifier")
-    return false
-  }
-
-  method.kind = "method"
-  method.static = tryContextual("static")
-  let isGenerator = this.eat(tt.star)
+  const ecmaVersion = this.options.ecmaVersion
+  const node = this.startNode()
+  let keyName = ""
+  let isGenerator = false
   let isAsync = false
-  if (!isGenerator) {
-    if (this.options.ecmaVersion >= 8 && tryContextual("async", true)) {
+  let kind = "method"
+  let isStatic = false
+
+  if (this.eatContextual("static")) {
+    // Parse static init block
+    if (ecmaVersion >= 13 && this.eat(tt.braceL)) {
+      this.parseClassStaticBlock(node)
+      return node
+    }
+    if (this.isClassElementNameStart() || this.type === tt.star) {
+      isStatic = true
+    } else {
+      keyName = "static"
+    }
+  }
+  node.static = isStatic
+  if (!keyName && ecmaVersion >= 8 && this.eatContextual("async")) {
+    if ((this.isClassElementNameStart() || this.type === tt.star) && !this.canInsertSemicolon()) {
       isAsync = true
-      isGenerator = this.options.ecmaVersion >= 9 && this.eat(tt.star)
-    } else if (tryContextual("get")) {
-      method.kind = "get"
-    } else if (tryContextual("set")) {
-      method.kind = "set"
+    } else {
+      keyName = "async"
+    }
+  }
+  if (!keyName && (ecmaVersion >= 9 || !isAsync) && this.eat(tt.star)) {
+    isGenerator = true
+  }
+  if (!keyName && !isAsync && !isGenerator) {
+    const lastValue = this.value
+    if (this.eatContextual("get") || this.eatContextual("set")) {
+      if (this.isClassElementNameStart()) {
+        kind = lastValue
+      } else {
+        keyName = lastValue
+      }
+    }
+  }
+
+  // Parse element name
+  if (keyName) {
+    // 'async', 'get', 'set', or 'static' were not a keyword contextually.
+    // The last token is any of those. Make it the element name.
+    node.computed = false
+    node.key = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc)
+    node.key.name = keyName
+    this.finishNode(node.key, "Identifier")
+  } else {
+    this.parseClassElementName(node)
+  }
+
+  // Parse element value
+  if (ecmaVersion < 13 || this.type === tt.parenL || kind !== "method" || isGenerator || isAsync) {
+    const isConstructor = !node.static && checkKeyName(node, "constructor")
+    const allowsDirectSuper = isConstructor && constructorAllowsSuper
+    // Couldn't move this check into the 'parseClassMethod' method for backward compatibility.
+    if (isConstructor && kind !== "method") this.raise(node.key.start, "Constructor can't have get/set modifier")
+    node.kind = isConstructor ? "constructor" : kind
+    this.parseClassMethod(node, isGenerator, isAsync, allowsDirectSuper)
+  } else {
+    this.parseClassField(node)
+  }
+
+  return node
+}
+
+pp.isClassElementNameStart = function() {
+  return (
+    this.type === tt.name ||
+    this.type === tt.privateId ||
+    this.type === tt.num ||
+    this.type === tt.string ||
+    this.type === tt.bracketL ||
+    this.type.keyword
+  )
+}
+
+pp.parseClassElementName = function(element) {
+  if (this.type === tt.privateId) {
+    if (this.value === "constructor") {
+      this.raise(this.start, "Classes can't have an element named '#constructor'")
     }
+    element.computed = false
+    element.key = this.parsePrivateIdent()
+  } else {
+    this.parsePropertyName(element)
   }
-  if (!method.key) this.parsePropertyName(method)
-  let {key} = method
-  let allowsDirectSuper = false
-  if (!method.computed && !method.static && (key.type === "Identifier" && key.name === "constructor" ||
-      key.type === "Literal" && key.value === "constructor")) {
-    if (method.kind !== "method") this.raise(key.start, "Constructor can't have get/set modifier")
+}
+
+pp.parseClassMethod = function(method, isGenerator, isAsync, allowsDirectSuper) {
+  // Check key and flags
+  const key = method.key
+  if (method.kind === "constructor") {
     if (isGenerator) this.raise(key.start, "Constructor can't be a generator")
     if (isAsync) this.raise(key.start, "Constructor can't be an async method")
-    method.kind = "constructor"
-    allowsDirectSuper = constructorAllowsSuper
-  } else if (method.static && key.type === "Identifier" && key.name === "prototype") {
+  } else if (method.static && checkKeyName(method, "prototype")) {
     this.raise(key.start, "Classes may not have a static property named prototype")
   }
-  this.parseClassMethod(method, isGenerator, isAsync, allowsDirectSuper)
-  if (method.kind === "get" && method.value.params.length !== 0)
-    this.raiseRecoverable(method.value.start, "getter should have no params")
-  if (method.kind === "set" && method.value.params.length !== 1)
-    this.raiseRecoverable(method.value.start, "setter should have exactly one param")
-  if (method.kind === "set" && method.value.params[0].type === "RestElement")
-    this.raiseRecoverable(method.value.params[0].start, "Setter cannot use rest params")
-  return method
-}
 
-pp.parseClassMethod = function(method, isGenerator, isAsync, allowsDirectSuper) {
-  method.value = this.parseMethod(isGenerator, isAsync, allowsDirectSuper)
+  // Parse value
+  const value = method.value = this.parseMethod(isGenerator, isAsync, allowsDirectSuper)
+
+  // Check value
+  if (method.kind === "get" && value.params.length !== 0)
+    this.raiseRecoverable(value.start, "getter should have no params")
+  if (method.kind === "set" && value.params.length !== 1)
+    this.raiseRecoverable(value.start, "setter should have exactly one param")
+  if (method.kind === "set" && value.params[0].type === "RestElement")
+    this.raiseRecoverable(value.params[0].start, "Setter cannot use rest params")
+
   return this.finishNode(method, "MethodDefinition")
 }
 
+pp.parseClassField = function(field) {
+  if (checkKeyName(field, "constructor")) {
+    this.raise(field.key.start, "Classes can't have a field named 'constructor'")
+  } else if (field.static && checkKeyName(field, "prototype")) {
+    this.raise(field.key.start, "Classes can't have a static field named 'prototype'")
+  }
+
+  if (this.eat(tt.eq)) {
+    // To raise SyntaxError if 'arguments' exists in the initializer.
+    const scope = this.currentThisScope()
+    const inClassFieldInit = scope.inClassFieldInit
+    scope.inClassFieldInit = true
+    field.value = this.parseMaybeAssign()
+    scope.inClassFieldInit = inClassFieldInit
+  } else {
+    field.value = null
+  }
+  this.semicolon()
+
+  return this.finishNode(field, "PropertyDefinition")
+}
+
+pp.parseClassStaticBlock = function(node) {
+  node.body = []
+
+  let oldLabels = this.labels
+  this.labels = []
+  this.enterScope(SCOPE_CLASS_STATIC_BLOCK | SCOPE_SUPER)
+  while (this.type !== tt.braceR) {
+    let stmt = this.parseStatement(null)
+    node.body.push(stmt)
+  }
+  this.next()
+  this.exitScope()
+  this.labels = oldLabels
+
+  return this.finishNode(node, "StaticBlock")
+}
+
 pp.parseClassId = function(node, isStatement) {
-  node.id = this.type === tt.name ? this.parseIdent() : isStatement === true ? this.unexpected() : null
+  if (this.type === tt.name) {
+    node.id = this.parseIdent()
+    if (isStatement)
+      this.checkLValSimple(node.id, BIND_LEXICAL, false)
+  } else {
+    if (isStatement === true)
+      this.unexpected()
+    node.id = null
+  }
 }
 
 pp.parseClassSuper = function(node) {
-  node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null
+  node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts(false) : null
+}
+
+pp.enterClassBody = function() {
+  const element = {declared: Object.create(null), used: []}
+  this.privateNameStack.push(element)
+  return element.declared
+}
+
+pp.exitClassBody = function() {
+  const {declared, used} = this.privateNameStack.pop()
+  const len = this.privateNameStack.length
+  const parent = len === 0 ? null : this.privateNameStack[len - 1]
+  for (let i = 0; i < used.length; ++i) {
+    const id = used[i]
+    if (!hasOwn(declared, id.name)) {
+      if (parent) {
+        parent.used.push(id)
+      } else {
+        this.raiseRecoverable(id.start, `Private field '#${id.name}' must be declared in an enclosing class`)
+      }
+    }
+  }
+}
+
+function isPrivateNameConflicted(privateNameMap, element) {
+  const name = element.key.name
+  const curr = privateNameMap[name]
+
+  let next = "true"
+  if (element.type === "MethodDefinition" && (element.kind === "get" || element.kind === "set")) {
+    next = (element.static ? "s" : "i") + element.kind
+  }
+
+  // `class { get #a(){}; static set #a(_){} }` is also conflict.
+  if (
+    curr === "iget" && next === "iset" ||
+    curr === "iset" && next === "iget" ||
+    curr === "sget" && next === "sset" ||
+    curr === "sset" && next === "sget"
+  ) {
+    privateNameMap[name] = "true"
+    return false
+  } else if (!curr) {
+    privateNameMap[name] = next
+    return false
+  } else {
+    return true
+  }
+}
+
+function checkKeyName(node, name) {
+  const {computed, key} = node
+  return !computed && (
+    key.type === "Identifier" && key.name === name ||
+    key.type === "Literal" && key.value === name
+  )
 }
 
 // Parses module export declaration.
@@ -620,6 +838,14 @@ pp.parseExport = function(node, exports) {
   this.next()
   // export * from '...'
   if (this.eat(tt.star)) {
+    if (this.options.ecmaVersion >= 11) {
+      if (this.eatContextual("as")) {
+        node.exported = this.parseModuleExportName()
+        this.checkExport(exports, node.exported, this.lastTokStart)
+      } else {
+        node.exported = null
+      }
+    }
     this.expectContextual("from")
     if (this.type !== tt.string) this.unexpected()
     node.source = this.parseExprAtom()
@@ -633,7 +859,7 @@ pp.parseExport = function(node, exports) {
       let fNode = this.startNode()
       this.next()
       if (isAsync) this.next()
-      node.declaration = this.parseFunction(fNode, FUNC_STATEMENT | FUNC_NULLABLE_ID, false, isAsync, true)
+      node.declaration = this.parseFunction(fNode, FUNC_STATEMENT | FUNC_NULLABLE_ID, false, isAsync)
     } else if (this.type === tt._class) {
       let cNode = this.startNode()
       node.declaration = this.parseClass(cNode, "nullableID")
@@ -649,7 +875,7 @@ pp.parseExport = function(node, exports) {
     if (node.declaration.type === "VariableDeclaration")
       this.checkVariableExport(exports, node.declaration.declarations)
     else
-      this.checkExport(exports, node.declaration.id.name, node.declaration.id.start)
+      this.checkExport(exports, node.declaration.id, node.declaration.id.start)
     node.specifiers = []
     node.source = null
   } else { // export { x, y as z } [from '...']
@@ -659,9 +885,15 @@ pp.parseExport = function(node, exports) {
       if (this.type !== tt.string) this.unexpected()
       node.source = this.parseExprAtom()
     } else {
-      // check for keywords used as local names
       for (let spec of node.specifiers) {
+        // check for keywords used as local names
         this.checkUnreserved(spec.local)
+        // check if export is defined
+        this.checkLocalExport(spec.local)
+
+        if (spec.local.type === "Literal") {
+          this.raise(spec.local.start, "A string literal cannot be used as an exported binding without `from`.")
+        }
       }
 
       node.source = null
@@ -673,7 +905,9 @@ pp.parseExport = function(node, exports) {
 
 pp.checkExport = function(exports, name, pos) {
   if (!exports) return
-  if (has(exports, name))
+  if (typeof name !== "string")
+    name = name.type === "Identifier" ? name.name : name.value
+  if (hasOwn(exports, name))
     this.raiseRecoverable(pos, "Duplicate export '" + name + "'")
   exports[name] = true
 }
@@ -681,7 +915,7 @@ pp.checkExport = function(exports, name, pos) {
 pp.checkPatternExport = function(exports, pat) {
   let type = pat.type
   if (type === "Identifier")
-    this.checkExport(exports, pat.name, pat.start)
+    this.checkExport(exports, pat, pat.start)
   else if (type === "ObjectPattern")
     for (let prop of pat.properties)
       this.checkPatternExport(exports, prop)
@@ -727,9 +961,13 @@ pp.parseExportSpecifiers = function(exports) {
     } else first = false
 
     let node = this.startNode()
-    node.local = this.parseIdent(true)
-    node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local
-    this.checkExport(exports, node.exported.name, node.exported.start)
+    node.local = this.parseModuleExportName()
+    node.exported = this.eatContextual("as") ? this.parseModuleExportName() : node.local
+    this.checkExport(
+      exports,
+      node.exported,
+      node.exported.start
+    )
     nodes.push(this.finishNode(node, "ExportSpecifier"))
   }
   return nodes
@@ -760,7 +998,7 @@ pp.parseImportSpecifiers = function() {
     // import defaultObj, { x, y as z } from '...'
     let node = this.startNode()
     node.local = this.parseIdent()
-    this.checkLVal(node.local, BIND_LEXICAL)
+    this.checkLValSimple(node.local, BIND_LEXICAL)
     nodes.push(this.finishNode(node, "ImportDefaultSpecifier"))
     if (!this.eat(tt.comma)) return nodes
   }
@@ -769,7 +1007,7 @@ pp.parseImportSpecifiers = function() {
     this.next()
     this.expectContextual("as")
     node.local = this.parseIdent()
-    this.checkLVal(node.local, BIND_LEXICAL)
+    this.checkLValSimple(node.local, BIND_LEXICAL)
     nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"))
     return nodes
   }
@@ -781,19 +1019,30 @@ pp.parseImportSpecifiers = function() {
     } else first = false
 
     let node = this.startNode()
-    node.imported = this.parseIdent(true)
+    node.imported = this.parseModuleExportName()
     if (this.eatContextual("as")) {
       node.local = this.parseIdent()
     } else {
       this.checkUnreserved(node.imported)
       node.local = node.imported
     }
-    this.checkLVal(node.local, BIND_LEXICAL)
+    this.checkLValSimple(node.local, BIND_LEXICAL)
     nodes.push(this.finishNode(node, "ImportSpecifier"))
   }
   return nodes
 }
 
+pp.parseModuleExportName = function() {
+  if (this.options.ecmaVersion >= 13 && this.type === tt.string) {
+    const stringLiteral = this.parseLiteral(this.value)
+    if (loneSurrogate.test(stringLiteral.value)) {
+      this.raise(stringLiteral.start, "An export name cannot include a lone surrogate.")
+    }
+    return stringLiteral
+  }
+  return this.parseIdent(true)
+}
+
 // Set `ExpressionStatement#directive` property for directive prologues.
 pp.adaptDirectivePrologue = function(statements) {
   for (let i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {