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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174 | 1
1
1
1
1
1
55956
55956
55956
55956
55956
1
1
1
7147
5993
1
3
3
3
1
55956
55956
2054
2054
2054
2054
5389
5389
2054
94
188
188
94
2054
1434
2424
2054
33
2054
926
22156
74
148
148
22156
21815
21521
3631
21521
559
2054
584
584
413
2054
1
72
72
72
72
72
1
1
190
1
14318
14318
14318
1
250
1
14094
1
14128
1
13501
| module.exports = Scope;
var traverse = require("./index");
var t = require("../types");
var _ = require("lodash");
var FOR_KEYS = ["left", "init"];
/**
* This searches the current "scope" and collects all references/declarations
* within.
*
* @param {Node} block
* @param {Scope} [parent]
*/
function Scope(block, parent) {
this.parent = parent;
this.block = block;
var info = this.getInfo();
this.references = info.references;
this.declarations = info.declarations;
}
var vars = require("jshint/src/vars");
Scope.defaultDeclarations = _.flatten([vars.newEcmaIdentifiers, vars.node, vars.ecmaIdentifiers, vars.reservedVars].map(_.keys));
Scope.add = function (node, references) {
if (!node) return;
_.defaults(references, t.getIds(node, true));
};
Scope.prototype.generateTemp = function (file, name) {
var id = file.generateUidIdentifier(name || "temp", this);
this.push({
key: id.name,
id: id
});
return id;
};
Scope.prototype.getInfo = function () {
var block = this.block;
if (block._scopeInfo) return block._scopeInfo;
var info = block._scopeInfo = {};
var references = info.references = {};
var declarations = info.declarations = {};
var add = function (node, reference) {
Scope.add(node, references);
if (!reference) Scope.add(node, declarations);
};
// ForStatement - left, init
if (t.isFor(block)) {
_.each(FOR_KEYS, function (key) {
var node = block[key];
if (t.isLet(node)) add(node);
});
block = block.body;
}
// Program, BlockStatement - let variables
if (t.isBlockStatement(block) || t.isProgram(block)) {
_.each(block.body, function (node) {
// check for non-var `VariableDeclaration`s
if (t.isLet(node)) add(node);
});
}
// CatchClause - param
if (t.isCatchClause(block)) {
add(block.param);
}
// Program, Function - var variables
if (t.isProgram(block) || t.isFunction(block)) {
traverse(block, function (node, parent, scope) {
if (t.isFor(node)) {
_.each(FOR_KEYS, function (key) {
var declar = node[key];
if (t.isVar(declar)) add(declar);
});
}
// this block is a function so we'll stop since none of the variables
// declared within are accessible
if (t.isFunction(node)) return false;
// function identifier doesn't belong to this scope
if (block.id && node === block.id) return;
if (t.isIdentifier(node) && t.isReferenced(node, parent) && !scope.has(node.name)) {
add(node, true);
}
// we've ran into a declaration!
// we'll let the BlockStatement scope deal with `let` declarations unless
if (t.isDeclaration(node) && !t.isLet(node)) {
add(node);
}
}, { scope: this });
}
// Function - params, rest
if (t.isFunction(block)) {
add(block.rest);
_.each(block.params, function (param) {
add(param);
});
}
return info;
};
Scope.prototype.push = function (opts) {
var block = this.block;
Iif (t.isFor(block) || t.isCatchClause(block) || t.isFunction(block)) {
t.ensureBlock(block);
block = block.body;
}
Eif (t.isBlockStatement(block) || t.isProgram(block)) {
block._declarations = block._declarations || {};
block._declarations[opts.key] = {
kind: opts.kind,
id: opts.id,
init: opts.init
};
} else {
throw new TypeError("cannot add a declaration here in node type " + block.type);
}
};
Scope.prototype.add = function (node) {
Scope.add(node, this.references);
};
Scope.prototype.get = function (id, decl) {
return id && (this.getOwn(id, decl) || this.parentGet(id, decl));
};
Scope.prototype.getOwn = function (id, decl) {
var refs = this.references;
if (decl) refs = this.declarations;
return _.has(refs, id) && refs[id];
};
Scope.prototype.parentGet = function (id, decl) {
return this.parent && this.parent.get(id, decl);
};
Scope.prototype.has = function (id, decl) {
return (id && (this.hasOwn(id, decl) || this.parentHas(id, decl))) ||
_.contains(Scope.defaultDeclarations, id);
};
Scope.prototype.hasOwn = function (id, decl) {
return !!this.getOwn(id, decl);
};
Scope.prototype.parentHas = function (id, decl) {
return this.parent && this.parent.has(id, decl);
};
|