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
175
176 | 1
1
1
1
1
1
58253
58253
58253
58253
58253
1
1
1
8255
6951
1
3
3
3
1
58253
58253
2262
2262
2262
2262
6141
6141
2262
106
212
212
106
2262
1567
2842
2262
33
2262
1013
24427
86
172
172
24427
24043
23744
4027
23744
723
2262
659
659
503
2262
1
72
72
72
72
72
1
1
211
1
16253
16253
16253
1
257
1
16008
1
16042
1
15362
| 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, {
enter: 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);
}
}
}, 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);
};
|