1 import {Parser} from "./state.js"
2 import {SCOPE_VAR, SCOPE_FUNCTION, SCOPE_TOP, SCOPE_ARROW, SCOPE_SIMPLE_CATCH, BIND_LEXICAL, BIND_SIMPLE_CATCH, BIND_FUNCTION} from "./scopeflags.js"
4 const pp = Parser.prototype
9 // A list of var-declared names in the current lexical scope
11 // A list of lexically-declared names in the current lexical scope
13 // A list of lexically-declared FunctionDeclaration names in the current lexical scope
15 // A switch to disallow the identifier reference 'arguments'
16 this.inClassFieldInit = false
20 // The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.
22 pp.enterScope = function(flags) {
23 this.scopeStack.push(new Scope(flags))
26 pp.exitScope = function() {
31 // > At the top level of a function, or script, function declarations are
32 // > treated like var declarations rather than like lexical declarations.
33 pp.treatFunctionsAsVarInScope = function(scope) {
34 return (scope.flags & SCOPE_FUNCTION) || !this.inModule && (scope.flags & SCOPE_TOP)
37 pp.declareName = function(name, bindingType, pos) {
38 let redeclared = false
39 if (bindingType === BIND_LEXICAL) {
40 const scope = this.currentScope()
41 redeclared = scope.lexical.indexOf(name) > -1 || scope.functions.indexOf(name) > -1 || scope.var.indexOf(name) > -1
42 scope.lexical.push(name)
43 if (this.inModule && (scope.flags & SCOPE_TOP))
44 delete this.undefinedExports[name]
45 } else if (bindingType === BIND_SIMPLE_CATCH) {
46 const scope = this.currentScope()
47 scope.lexical.push(name)
48 } else if (bindingType === BIND_FUNCTION) {
49 const scope = this.currentScope()
50 if (this.treatFunctionsAsVar)
51 redeclared = scope.lexical.indexOf(name) > -1
53 redeclared = scope.lexical.indexOf(name) > -1 || scope.var.indexOf(name) > -1
54 scope.functions.push(name)
56 for (let i = this.scopeStack.length - 1; i >= 0; --i) {
57 const scope = this.scopeStack[i]
58 if (scope.lexical.indexOf(name) > -1 && !((scope.flags & SCOPE_SIMPLE_CATCH) && scope.lexical[0] === name) ||
59 !this.treatFunctionsAsVarInScope(scope) && scope.functions.indexOf(name) > -1) {
64 if (this.inModule && (scope.flags & SCOPE_TOP))
65 delete this.undefinedExports[name]
66 if (scope.flags & SCOPE_VAR) break
69 if (redeclared) this.raiseRecoverable(pos, `Identifier '${name}' has already been declared`)
72 pp.checkLocalExport = function(id) {
73 // scope.functions must be empty as Module code is always strict.
74 if (this.scopeStack[0].lexical.indexOf(id.name) === -1 &&
75 this.scopeStack[0].var.indexOf(id.name) === -1) {
76 this.undefinedExports[id.name] = id
80 pp.currentScope = function() {
81 return this.scopeStack[this.scopeStack.length - 1]
84 pp.currentVarScope = function() {
85 for (let i = this.scopeStack.length - 1;; i--) {
86 let scope = this.scopeStack[i]
87 if (scope.flags & SCOPE_VAR) return scope
91 // Could be useful for `this`, `new.target`, `super()`, `super.property`, and `super[property]`.
92 pp.currentThisScope = function() {
93 for (let i = this.scopeStack.length - 1;; i--) {
94 let scope = this.scopeStack[i]
95 if (scope.flags & SCOPE_VAR && !(scope.flags & SCOPE_ARROW)) return scope