aboutsummaryrefslogtreecommitdiff
path: root/node_modules/acorn/src/scope.js
blob: 2ec0448990b37d8645d14532523bdd63e698e808 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import {Parser} from "./state"
import {has} from "./util"

const pp = Parser.prototype

// Object.assign polyfill
const assign = Object.assign || function(target, ...sources) {
  for (let i = 0; i < sources.length; i++) {
    const source = sources[i]
    for (const key in source) {
      if (has(source, key)) {
        target[key] = source[key]
      }
    }
  }
  return target
}

// The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.

pp.enterFunctionScope = function() {
  // var: a hash of var-declared names in the current lexical scope
  // lexical: a hash of lexically-declared names in the current lexical scope
  // childVar: a hash of var-declared names in all child lexical scopes of the current lexical scope (within the current function scope)
  // parentLexical: a hash of lexically-declared names in all parent lexical scopes of the current lexical scope (within the current function scope)
  this.scopeStack.push({var: {}, lexical: {}, childVar: {}, parentLexical: {}})
}

pp.exitFunctionScope = function() {
  this.scopeStack.pop()
}

pp.enterLexicalScope = function() {
  const parentScope = this.scopeStack[this.scopeStack.length - 1]
  const childScope = {var: {}, lexical: {}, childVar: {}, parentLexical: {}}

  this.scopeStack.push(childScope)
  assign(childScope.parentLexical, parentScope.lexical, parentScope.parentLexical)
}

pp.exitLexicalScope = function() {
  const childScope = this.scopeStack.pop()
  const parentScope = this.scopeStack[this.scopeStack.length - 1]

  assign(parentScope.childVar, childScope.var, childScope.childVar)
}

/**
 * A name can be declared with `var` if there are no variables with the same name declared with `let`/`const`
 * in the current lexical scope or any of the parent lexical scopes in this function.
 */
pp.canDeclareVarName = function(name) {
  const currentScope = this.scopeStack[this.scopeStack.length - 1]

  return !has(currentScope.lexical, name) && !has(currentScope.parentLexical, name)
}

/**
 * A name can be declared with `let`/`const` if there are no variables with the same name declared with `let`/`const`
 * in the current scope, and there are no variables with the same name declared with `var` in the current scope or in
 * any child lexical scopes in this function.
 */
pp.canDeclareLexicalName = function(name) {
  const currentScope = this.scopeStack[this.scopeStack.length - 1]

  return !has(currentScope.lexical, name) && !has(currentScope.var, name) && !has(currentScope.childVar, name)
}

pp.declareVarName = function(name) {
  this.scopeStack[this.scopeStack.length - 1].var[name] = true
}

pp.declareLexicalName = function(name) {
  this.scopeStack[this.scopeStack.length - 1].lexical[name] = true
}