aboutsummaryrefslogtreecommitdiff
path: root/lib/scope.js
blob: 4c0aa1058068cdd26474c6168a5160f25d2b176f (about) (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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
AST_Scope.DEFMETHOD("figure_out_scope", function(){
    // This does what ast_add_scope did in UglifyJS v1.
    //
    // Part of it could be done at parse time, but it would complicate
    // the parser (and it's already kinda complex).  It's also worth
    // having it separated because we might need to call it multiple
    // times on the same tree.

    // pass 1: setup scope chaining and handle definitions
    var scope = null;
    var tw = new TreeWalker(function(node, descend){
        if (node instanceof AST_Scope) {
            var save_scope = node.parent_scope = scope;
            scope = node;
            descend.call(node);
            scope = save_scope;
            return true;        // don't descend again in TreeWalker
        }
        if (node instanceof AST_With) {
            for (var s = scope; s; s = s.parent_scope)
                s.uses_with = true;
            return;
        }
        if (node instanceof AST_SymbolDeclaration && !scope.parent_scope) {
            node.global = true;
        }
        if (node instanceof AST_SymbolVar) {
            scope.def_variable(node);
        }
        else if (node instanceof AST_SymbolLambda) {
            scope.def_function(node);
        }
        else if (node instanceof AST_SymbolDefun) {
            scope.parent_scope.def_function(node);
        }
        else if (node instanceof AST_Label) {
            scope.def_label(node);
        }
        else if (node instanceof AST_SymbolCatch) {
            // XXX: this is wrong according to ECMA-262 (12.4).  the
            // `catch` argument name should be visible only inside the
            // catch block.  For a quick fix AST_Catch should inherit
            // from AST_Scope.
            scope.def_variable(node);
        }
        else if (node instanceof AST_SymbolRef) {
            node.scope = scope;
        }
    });
    this.walk(tw);

    // pass 2: find back references and eval/with
    var tw = new TreeWalker(function(node){
        if (node instanceof AST_LabelRef) {
            var sym = node.scope.find_label(node);
            if (!sym) throw new Error("Undefined label " + node.name);
            node.reference(sym);
        }
        else if (node instanceof AST_SymbolRef) {
            var sym = node.scope.find_variable(node);
            if (!sym) {
                if (node.name == "eval") {
                    for (var s = scope; s; s = s.parent_scope)
                        s.uses_eval = true;
                }
            } else {
                node.reference(sym);
            }
        }
    });
    this.walk(tw);
});

AST_Scope.DEFMETHOD("find_variable", function(name){
    if (name instanceof AST_Symbol) name = name.name;
    return this.variables[name] ||
        (this.name && this.name.name == name && this.name) ||
        (this.parent_scope && this.parent_scope.find_variable(name));
});

AST_Scope.DEFMETHOD("find_label", function(name){
    if (name instanceof AST_Symbol) name = name.name;
    return this.labels[name];
});

AST_Scope.DEFMETHOD("def_function", function(symbol){
    this.def_variable(symbol);
    this.functions[symbol.name] = symbol;
    symbol.scope = this;
});

AST_Scope.DEFMETHOD("def_variable", function(symbol){
    this.variables[symbol.name] = symbol;
    delete this.functions[symbol.name];
    symbol.scope = this;
});

AST_Scope.DEFMETHOD("def_label", function(symbol){
    this.labels[symbol.name] = symbol;
    symbol.scope = this;
});