|
Lines 31-39
Link Here
|
| 31 |
scopes: [], |
31 |
scopes: [], |
| 32 |
context: null, |
32 |
context: null, |
| 33 |
thisCheck: false, |
33 |
thisCheck: false, |
| 34 |
callExpressions: [], |
|
|
| 35 |
GENERAL: 1, |
| 36 |
FUNCTION: 2, |
| 37 |
|
34 |
|
| 38 |
/** |
35 |
/** |
| 39 |
* @name enter |
36 |
* @name enter |
|
Lines 45-186
Link Here
|
| 45 |
* @returns The status if we should continue visiting |
42 |
* @returns The status if we should continue visiting |
| 46 |
*/ |
43 |
*/ |
| 47 |
enter: function(node) { |
44 |
enter: function(node) { |
| 48 |
if (node.type === Estraverse.Syntax.Program){ |
45 |
switch(node.type) { |
| 49 |
this.callExpressions = []; |
46 |
case Estraverse.Syntax.Program: |
| 50 |
this.occurrences = []; |
47 |
this.occurrences = []; |
| 51 |
this.scopes = [{range: node.range, occurrences: []}]; |
48 |
this.scopes = [{range: node.range, occurrences: []}]; |
| 52 |
this.defnode = null; |
49 |
this.defnode = null; |
| 53 |
this.defscope = null; |
50 |
this.defscope = null; |
| 54 |
} else if (this.thisCheck) { |
51 |
break; |
| 55 |
return this.enterThis(node); |
52 |
case Estraverse.Syntax.FunctionDeclaration: |
| 56 |
} else { |
53 |
this.checkId(node.id, true); |
| 57 |
switch(node.type) { |
54 |
//we want the parent scope for a declaration, otherwise we leave it right away |
| 58 |
case Estraverse.Syntax.FunctionDeclaration: |
55 |
this._enterScope(node); |
| 59 |
this.checkId(node.id, this.FUNCTION, true); |
56 |
if (node.params) { |
| 60 |
//we want the parent scope for a declaration, otherwise we leave it right away |
57 |
for (var i = 0; i < node.params.length; i++) { |
| 61 |
this.scopes.push({range: node.range, occurrences: []}); |
58 |
if(this.checkId(node.params[i], true)) { |
| 62 |
if (node.params) { |
59 |
return Estraverse.VisitorOption.Skip; |
| 63 |
for (var i = 0; i < node.params.length; i++) { |
|
|
| 64 |
if(this.checkId(node.params[i], this.GENERAL, true)) { |
| 65 |
return Estraverse.VisitorOption.Skip; |
| 66 |
} |
| 67 |
} |
60 |
} |
| 68 |
} |
61 |
} |
| 69 |
break; |
62 |
} |
| 70 |
case Estraverse.Syntax.FunctionExpression: |
63 |
break; |
| 71 |
if (node.params) { |
64 |
case Estraverse.Syntax.FunctionExpression: |
| 72 |
this.scopes.push({range: node.range, occurrences: []}); |
65 |
if (node.params) { |
| 73 |
for (var j = 0; j < node.params.length; j++) { |
66 |
if(this._enterScope(node)) { |
| 74 |
if(this.checkId(node.params[j], this.GENERAL, true)) { |
67 |
return Estraverse.VisitorOption.Skip; |
| 75 |
return Estraverse.VisitorOption.Skip; |
68 |
} |
| 76 |
} |
69 |
for (var j = 0; j < node.params.length; j++) { |
|
|
70 |
if(this.checkId(node.params[j], true)) { |
| 71 |
return Estraverse.VisitorOption.Skip; |
| 77 |
} |
72 |
} |
| 78 |
} |
73 |
} |
| 79 |
break; |
74 |
} |
| 80 |
case Estraverse.Syntax.AssignmentExpression: |
75 |
break; |
| 81 |
this.checkId(node.left); |
76 |
case Estraverse.Syntax.AssignmentExpression: |
| 82 |
this.checkId(node.right); |
77 |
this.checkId(node.left); |
| 83 |
break; |
78 |
this.checkId(node.right); |
| 84 |
case Estraverse.Syntax.ArrayExpression: |
79 |
break; |
| 85 |
if (node.elements) { |
80 |
case Estraverse.Syntax.ArrayExpression: |
| 86 |
for (var k = 0; k < node.elements.length; k++) { |
81 |
if (node.elements) { |
| 87 |
this.checkId(node.elements[k]); |
82 |
for (var k = 0; k < node.elements.length; k++) { |
|
|
83 |
this.checkId(node.elements[k]); |
| 84 |
} |
| 85 |
} |
| 86 |
break; |
| 87 |
case Estraverse.Syntax.MemberExpression: |
| 88 |
this.checkId(node.object); |
| 89 |
if (node.computed) { //computed = true for [], false for . notation |
| 90 |
this.checkId(node.property); |
| 91 |
} |
| 92 |
break; |
| 93 |
case Estraverse.Syntax.BinaryExpression: |
| 94 |
this.checkId(node.left); |
| 95 |
this.checkId(node.right); |
| 96 |
break; |
| 97 |
case Estraverse.Syntax.UnaryExpression: |
| 98 |
this.checkId(node.argument); |
| 99 |
break; |
| 100 |
case Estraverse.Syntax.IfStatement: |
| 101 |
this.checkId(node.test); |
| 102 |
break; |
| 103 |
case Estraverse.Syntax.SwitchStatement: |
| 104 |
this.checkId(node.discriminant); |
| 105 |
break; |
| 106 |
case Estraverse.Syntax.UpdateExpression: |
| 107 |
this.checkId(node.argument); |
| 108 |
break; |
| 109 |
case Estraverse.Syntax.ConditionalExpression: |
| 110 |
this.checkId(node.test); |
| 111 |
this.checkId(node.consequent); |
| 112 |
this.checkId(node.alternate); |
| 113 |
break; |
| 114 |
case Estraverse.Syntax.CallExpression: |
| 115 |
this.checkId(node.callee, false); |
| 116 |
if (node.arguments) { |
| 117 |
for (var l = 0; l < node.arguments.length; l++) { |
| 118 |
this.checkId(node.arguments[l]); |
| 119 |
} |
| 120 |
} |
| 121 |
break; |
| 122 |
case Estraverse.Syntax.ReturnStatement: |
| 123 |
this.checkId(node.argument); |
| 124 |
break; |
| 125 |
case Estraverse.Syntax.ObjectExpression: |
| 126 |
if(this._enterScope(node)) { |
| 127 |
return Estraverse.VisitorOption.Skip; |
| 128 |
} |
| 129 |
if(node.properties) { |
| 130 |
var len = node.properties.length; |
| 131 |
for (var m = 0; m < len; m++) { |
| 132 |
var prop = node.properties[m]; |
| 133 |
if(this.thisCheck && prop.value && prop.value.type === Estraverse.Syntax.FunctionExpression) { |
| 134 |
//tag it |
| 135 |
prop.value.isprop = true; |
| 88 |
} |
136 |
} |
|
|
137 |
this.checkId(prop.value); |
| 89 |
} |
138 |
} |
| 90 |
break; |
139 |
} |
| 91 |
case Estraverse.Syntax.MemberExpression: |
140 |
break; |
| 92 |
this.checkId(node.object); |
141 |
case Estraverse.Syntax.VariableDeclarator: |
| 93 |
if (node.computed) { //computed = true for [], false for . notation |
142 |
this.checkId(node.id, true); |
| 94 |
this.checkId(node.property); |
143 |
this.checkId(node.init); |
|
|
144 |
break; |
| 145 |
case Estraverse.Syntax.NewExpression: |
| 146 |
this.checkId(node.callee, false); |
| 147 |
break; |
| 148 |
case Estraverse.Syntax.LogicalExpression: |
| 149 |
this.checkId(node.left); |
| 150 |
this.checkId(node.right); |
| 151 |
break; |
| 152 |
case Estraverse.Syntax.ThisExpression: |
| 153 |
if(this.thisCheck) { |
| 154 |
var scope = this.scopes[this.scopes.length-1]; |
| 155 |
scope.occurrences.push({ |
| 156 |
start: node.range[0], |
| 157 |
end: node.range[1] |
| 158 |
}); |
| 159 |
// if this node is the selected this we are in the right scope |
| 160 |
if (node.range[0] === this.context.node.range[0]){ |
| 161 |
this.defscope = scope; |
| 95 |
} |
162 |
} |
| 96 |
break; |
163 |
} |
| 97 |
case Estraverse.Syntax.BinaryExpression: |
164 |
break; |
| 98 |
this.checkId(node.left); |
|
|
| 99 |
this.checkId(node.right); |
| 100 |
break; |
| 101 |
case Estraverse.Syntax.UnaryExpression: |
| 102 |
this.checkId(node.argument); |
| 103 |
break; |
| 104 |
case Estraverse.Syntax.IfStatement: |
| 105 |
this.checkId(node.test); |
| 106 |
break; |
| 107 |
case Estraverse.Syntax.SwitchStatement: |
| 108 |
this.checkId(node.discriminant); |
| 109 |
break; |
| 110 |
case Estraverse.Syntax.UpdateExpression: |
| 111 |
this.checkId(node.argument); |
| 112 |
break; |
| 113 |
case Estraverse.Syntax.ConditionalExpression: |
| 114 |
this.checkId(node.test); |
| 115 |
this.checkId(node.consequent); |
| 116 |
this.checkId(node.alternate); |
| 117 |
break; |
| 118 |
case Estraverse.Syntax.CallExpression: |
| 119 |
this.checkId(node.callee, this.FUNCTION, false); |
| 120 |
if (node.arguments) { |
| 121 |
for (var l = 0; l < node.arguments.length; l++) { |
| 122 |
this.checkId(node.arguments[l]); |
| 123 |
} |
| 124 |
} |
| 125 |
break; |
| 126 |
case Estraverse.Syntax.ReturnStatement: |
| 127 |
this.checkId(node.argument); |
| 128 |
break; |
| 129 |
case Estraverse.Syntax.ObjectExpression: |
| 130 |
if(node.properties) { |
| 131 |
var len = node.properties.length; |
| 132 |
for (var m = 0; m < len; m++) { |
| 133 |
this.checkId(node.properties[m].value); |
| 134 |
} |
| 135 |
} |
| 136 |
break; |
| 137 |
case Estraverse.Syntax.VariableDeclarator: |
| 138 |
this.checkId(node.id, this.GENERAL, true); |
| 139 |
this.checkId(node.init); |
| 140 |
break; |
| 141 |
case Estraverse.Syntax.NewExpression: |
| 142 |
this.checkId(node.callee, this.FUNCTION, false); |
| 143 |
break; |
| 144 |
case Estraverse.Syntax.LogicalExpression: |
| 145 |
this.checkId(node.left); |
| 146 |
this.checkId(node.right); |
| 147 |
break; |
| 148 |
} |
| 149 |
} |
165 |
} |
| 150 |
}, |
166 |
}, |
| 151 |
|
167 |
|
| 152 |
/** |
168 |
/** |
| 153 |
* @name enterThis |
169 |
* @description Enters and records the current scope onthe scope stack |
| 154 |
* @description Callback from estraverse when a node is starting to be visited and we are searching for occurrences of 'this' |
|
|
| 155 |
* @function |
170 |
* @function |
| 156 |
* @private |
171 |
* @private |
| 157 |
* @param {Object} node The AST node currently being visited |
172 |
* @param {Object} node The AST node |
| 158 |
* @returns The status if we should continue visiting |
173 |
* @returns {Boolean} If we should skip visiting children of the scope node |
| 159 |
*/ |
174 |
*/ |
| 160 |
enterThis: function(node) { |
175 |
_enterScope: function(node) { |
| 161 |
// A function expression inside a call expression means a new scope for 'this' |
176 |
if(this.thisCheck) { |
| 162 |
if (node.type === Estraverse.Syntax.CallExpression){ |
177 |
switch(node.type) { |
| 163 |
this.callExpressions.push(node); |
178 |
case Estraverse.Syntax.ObjectExpression: |
| 164 |
} else if (node.type === Estraverse.Syntax.FunctionExpression){ |
179 |
this.scopes.push({range: node.range, occurrences: []}); |
| 165 |
if (this.callExpressions.length > 0){ |
180 |
if (this.defscope){ |
| 166 |
this.scopes.push({range: node.range, occurrences: []}); |
181 |
return true; |
| 167 |
// If the outer scope has the selected 'this' we can skip the inner scope |
182 |
} |
| 168 |
if (this.defscope){ |
183 |
break; |
| 169 |
return Estraverse.VisitorOption.Skip; |
184 |
case Estraverse.Syntax.FunctionExpression: |
| 170 |
} |
185 |
if (!node.isprop){ |
| 171 |
} |
186 |
this.scopes.push({range: node.range, occurrences: []}); |
| 172 |
} else if (node.type === Estraverse.Syntax.ThisExpression){ |
187 |
// If the outer scope has the selected 'this' we can skip the inner scope |
| 173 |
var scope = this.scopes[this.scopes.length-1]; |
188 |
if (this.defscope){ |
| 174 |
scope.occurrences.push({ |
189 |
return true; |
| 175 |
start: node.range[0], |
190 |
} |
| 176 |
end: node.range[1] |
191 |
} |
| 177 |
}); |
192 |
break; |
| 178 |
|
|
|
| 179 |
// if this node is the selected this we are in the right scope |
| 180 |
if (node.range === this.context.node.range){ |
| 181 |
this.defscope = scope; |
| 182 |
} |
193 |
} |
| 183 |
} |
194 |
} |
|
|
195 |
else { |
| 196 |
switch(node.type) { |
| 197 |
case Estraverse.Syntax.FunctionDeclaration: |
| 198 |
case Estraverse.Syntax.FunctionExpression: |
| 199 |
this.scopes.push({range: node.range, occurrences: []}); |
| 200 |
break; |
| 201 |
} |
| 202 |
} |
| 203 |
return false; |
| 184 |
}, |
204 |
}, |
| 185 |
|
205 |
|
| 186 |
/** |
206 |
/** |
|
Lines 193-245
Link Here
|
| 193 |
* @return The status if we should continue visiting |
213 |
* @return The status if we should continue visiting |
| 194 |
*/ |
214 |
*/ |
| 195 |
leave: function(node) { |
215 |
leave: function(node) { |
| 196 |
if (this.context.node.type === Estraverse.Syntax.ThisExpression){ |
216 |
if(this.thisCheck) { |
| 197 |
return this.leaveThis(node); |
217 |
switch(node.type) { |
| 198 |
} else { |
218 |
case Estraverse.Syntax.FunctionExpression: |
| 199 |
if (node.type === Estraverse.Syntax.FunctionDeclaration || |
219 |
if(node.isprop) { |
| 200 |
node.type === Estraverse.Syntax.FunctionExpression || |
220 |
delete node.isprop; //remove the tag |
| 201 |
node.type === Estraverse.Syntax.Program) { |
221 |
break; |
| 202 |
//if we leave the defining scope |
|
|
| 203 |
var scope = this.scopes.pop(); |
| 204 |
if(this.defscope) { |
| 205 |
var len = scope.occurrences.length; |
| 206 |
for(var i = 0; i < len; i++) { |
| 207 |
this.occurrences.push(scope.occurrences[i]); |
| 208 |
} |
222 |
} |
| 209 |
if(this.defscope.range[0] === scope.range[0] && this.defscope.range[1] === scope.range[1]) { |
223 |
//FALL-THROUGH |
| 210 |
//we just popped out of the scope the word was defined in, we can quit |
224 |
case Estraverse.Syntax.ObjectExpression: |
|
|
225 |
case Estraverse.Syntax.Program: |
| 226 |
if(this._popScope()) { |
| 227 |
//we left an object closure, end |
| 211 |
return Estraverse.VisitorOption.Break; |
228 |
return Estraverse.VisitorOption.Break; |
| 212 |
} |
229 |
} |
| 213 |
} |
230 |
break; |
|
|
231 |
} |
| 232 |
} |
| 233 |
else { |
| 234 |
switch(node.type) { |
| 235 |
case Estraverse.Syntax.FunctionExpression: |
| 236 |
case Estraverse.Syntax.FunctionDeclaration: |
| 237 |
case Estraverse.Syntax.Program: |
| 238 |
if(this._popScope()) { |
| 239 |
return Estraverse.VisitorOption.Break; |
| 240 |
} |
| 241 |
break; |
| 214 |
} |
242 |
} |
| 215 |
} |
243 |
} |
| 216 |
}, |
244 |
}, |
| 217 |
|
245 |
|
| 218 |
/** |
246 |
/** |
| 219 |
* @name leaveThis |
247 |
* @description Pops the tip of the scope stack off, adds occurrences (if any) and returns if we should |
| 220 |
* @description Callback when visitation of a node has completed and we are searching for occurrences of 'this' |
248 |
* quit visiting |
| 221 |
* @function |
249 |
* @function |
| 222 |
* @private |
250 |
* @private |
| 223 |
* @param {Object} node The AST node we are inspecting |
251 |
* @returns {Boolean} If we should quit visiting |
| 224 |
* @return The status if we should continue visiting |
|
|
| 225 |
*/ |
252 |
*/ |
| 226 |
leaveThis: function(node) { |
253 |
_popScope: function() { |
| 227 |
if (node.type === Estraverse.Syntax.CallExpression){ |
254 |
var scope = this.scopes.pop(); |
| 228 |
this.callExpressions.pop(); |
255 |
if(this.defscope) { |
| 229 |
} else if ((this.callExpressions.length > 0 && node.type === Estraverse.Syntax.FunctionExpression) || |
256 |
var len = scope.occurrences.length; |
| 230 |
node.type === Estraverse.Syntax.Program) { |
257 |
for(var i = 0; i < len; i++) { |
| 231 |
var scope = this.scopes.pop(); |
258 |
this.occurrences.push(scope.occurrences[i]); |
| 232 |
if(this.defscope) { |
259 |
} |
| 233 |
var len = scope.occurrences.length; |
260 |
if(this.defscope.range[0] === scope.range[0] && this.defscope.range[1] === scope.range[1]) { |
| 234 |
for(var i = 0; i < len; i++) { |
261 |
//we just popped out of the scope the node was defined in, we can quit |
| 235 |
this.occurrences.push(scope.occurrences[i]); |
262 |
return true; |
| 236 |
} |
|
|
| 237 |
if(this.defscope.range[0] === scope.range[0] && this.defscope.range[1] === scope.range[1]) { |
| 238 |
//we just popped out of the scope the word was defined in, we can quit |
| 239 |
return Estraverse.VisitorOption.Break; |
| 240 |
} |
| 241 |
} |
263 |
} |
| 242 |
} |
264 |
} |
|
|
265 |
return false; |
| 243 |
}, |
266 |
}, |
| 244 |
|
267 |
|
| 245 |
/** |
268 |
/** |
|
Lines 249-260
Link Here
|
| 249 |
* @private |
272 |
* @private |
| 250 |
* @memberof javascript.JavaScriptOccurrences.prototype |
273 |
* @memberof javascript.JavaScriptOccurrences.prototype |
| 251 |
* @param {Object} node The AST node we are inspecting |
274 |
* @param {Object} node The AST node we are inspecting |
| 252 |
* @param {Number} kind The kind of occurrence to consider |
|
|
| 253 |
* @param {Boolean} candefine If the given node can define the word we are looking for |
275 |
* @param {Boolean} candefine If the given node can define the word we are looking for |
| 254 |
* @returns {Boolean} <code>true</code> if we should skip the next nodes, <code>false</code> otherwise |
276 |
* @returns {Boolean} <code>true</code> if we should skip the next nodes, <code>false</code> otherwise |
| 255 |
*/ |
277 |
*/ |
| 256 |
checkId: function(node, kind, candefine) { |
278 |
checkId: function(node, candefine) { |
| 257 |
if (node && node.type === Estraverse.Syntax.Identifier) { |
279 |
if (!this.thisCheck && node && node.type === Estraverse.Syntax.Identifier) { |
| 258 |
if (node.name === this.context.word) { |
280 |
if (node.name === this.context.word) { |
| 259 |
var scope = this.scopes[this.scopes.length-1]; // Always will have at least the program scope |
281 |
var scope = this.scopes[this.scopes.length-1]; // Always will have at least the program scope |
| 260 |
if(candefine) { |
282 |
if(candefine) { |
|
Lines 271-277
Link Here
|
| 271 |
} |
293 |
} |
| 272 |
if(node.range[0] <= this.context.start && node.range[1] >= this.context.start) { |
294 |
if(node.range[0] <= this.context.start && node.range[1] >= this.context.start) { |
| 273 |
this.defnode = node.range; |
295 |
this.defnode = node.range; |
| 274 |
this.defnode.kind = !kind ? this.GENERAL : kind; |
|
|
| 275 |
} |
296 |
} |
| 276 |
} |
297 |
} |
| 277 |
scope.occurrences.push({ |
298 |
scope.occurrences.push({ |