You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
846 lines
28 KiB
JavaScript
846 lines
28 KiB
JavaScript
/*
|
|
Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
|
|
Copyright (C) 2013 Alex Seville <hi@alexanderseville.com>
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*jslint bitwise:true */
|
|
/*global exports:true, define:true, require:true*/
|
|
(function (factory, global) {
|
|
'use strict';
|
|
|
|
function namespace(str, obj) {
|
|
var i, iz, names, name;
|
|
names = str.split('.');
|
|
for (i = 0, iz = names.length; i < iz; ++i) {
|
|
name = names[i];
|
|
if (obj.hasOwnProperty(name)) {
|
|
obj = obj[name];
|
|
} else {
|
|
obj = (obj[name] = {});
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
|
|
// and plain browser loading,
|
|
if (typeof define === 'function' && define.amd) {
|
|
define('escope', ['exports', 'estraverse'], function (exports, estraverse) {
|
|
factory(exports, global, estraverse);
|
|
});
|
|
} else if (typeof exports !== 'undefined') {
|
|
factory(exports, global, require('estraverse'));
|
|
} else {
|
|
factory(namespace('escope', global), global, global.estraverse);
|
|
}
|
|
}(function (exports, global, estraverse) {
|
|
'use strict';
|
|
|
|
var Syntax,
|
|
Map,
|
|
currentScope,
|
|
globalScope,
|
|
scopes,
|
|
options;
|
|
|
|
Syntax = estraverse.Syntax;
|
|
|
|
if (typeof global.Map !== 'undefined') {
|
|
// ES6 Map
|
|
Map = global.Map;
|
|
} else {
|
|
Map = function Map() {
|
|
this.__data = {};
|
|
};
|
|
|
|
Map.prototype.get = function MapGet(key) {
|
|
key = '$' + key;
|
|
if (this.__data.hasOwnProperty(key)) {
|
|
return this.__data[key];
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
Map.prototype.has = function MapHas(key) {
|
|
key = '$' + key;
|
|
return this.__data.hasOwnProperty(key);
|
|
};
|
|
|
|
Map.prototype.set = function MapSet(key, val) {
|
|
key = '$' + key;
|
|
this.__data[key] = val;
|
|
};
|
|
|
|
Map.prototype['delete'] = function MapDelete(key) {
|
|
key = '$' + key;
|
|
return delete this.__data[key];
|
|
};
|
|
}
|
|
|
|
function assert(cond, text) {
|
|
if (!cond) {
|
|
throw new Error(text);
|
|
}
|
|
}
|
|
|
|
function defaultOptions() {
|
|
return {
|
|
optimistic: false,
|
|
directive: false
|
|
};
|
|
};
|
|
|
|
function updateDeeply(target, override) {
|
|
var key, val;
|
|
|
|
function isHashObject(target) {
|
|
return typeof target === 'object' && target instanceof Object && !(target instanceof RegExp);
|
|
}
|
|
|
|
for (key in override) {
|
|
if (override.hasOwnProperty(key)) {
|
|
val = override[key];
|
|
if (isHashObject(val)) {
|
|
if (isHashObject(target[key])) {
|
|
updateDeeply(target[key], val);
|
|
} else {
|
|
target[key] = updateDeeply({}, val);
|
|
}
|
|
} else {
|
|
target[key] = val;
|
|
}
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
|
|
function Reference(ident, scope, flag, writeExpr, maybeImplicitGlobal) {
|
|
this.identifier = ident;
|
|
this.from = scope;
|
|
this.tainted = false;
|
|
this.resolved = null;
|
|
this.flag = flag;
|
|
if (this.isWrite()) {
|
|
this.writeExpr = writeExpr;
|
|
}
|
|
this.__maybeImplicitGlobal = maybeImplicitGlobal;
|
|
}
|
|
|
|
Reference.READ = 0x1;
|
|
Reference.WRITE = 0x2;
|
|
Reference.RW = 0x3;
|
|
|
|
Reference.prototype.isStatic = function isStatic() {
|
|
return !this.tainted && this.resolved && this.resolved.scope.isStatic();
|
|
};
|
|
|
|
Reference.prototype.isWrite = function isWrite() {
|
|
return this.flag & Reference.WRITE;
|
|
};
|
|
|
|
Reference.prototype.isRead = function isRead() {
|
|
return this.flag & Reference.READ;
|
|
};
|
|
|
|
Reference.prototype.isReadOnly = function isReadOnly() {
|
|
return this.flag === Reference.READ;
|
|
};
|
|
|
|
Reference.prototype.isWriteOnly = function isWriteOnly() {
|
|
return this.flag === Reference.WRITE;
|
|
};
|
|
|
|
Reference.prototype.isReadWrite = function isReadWrite() {
|
|
return this.flag === Reference.RW;
|
|
};
|
|
|
|
function Variable(name, scope) {
|
|
this.name = name;
|
|
this.identifiers = [];
|
|
this.references = [];
|
|
|
|
this.defs = [];
|
|
|
|
this.tainted = false;
|
|
this.stack = true;
|
|
this.scope = scope;
|
|
}
|
|
|
|
Variable.CatchClause = 'CatchClause';
|
|
Variable.Parameter = 'Parameter';
|
|
Variable.FunctionName = 'FunctionName';
|
|
Variable.Variable = 'Variable';
|
|
Variable.ImplicitGlobalVariable = 'ImplicitGlobalVariable';
|
|
|
|
function isStrictScope(scope, block) {
|
|
var body, i, iz, stmt, expr;
|
|
|
|
// When upper scope is exists and strict, inner scope is also strict.
|
|
if (scope.upper && scope.upper.isStrict) {
|
|
return true;
|
|
}
|
|
|
|
if (scope.type === 'function') {
|
|
body = block.body;
|
|
} else if (scope.type === 'global') {
|
|
body = block;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if (options.directive) {
|
|
for (i = 0, iz = body.body.length; i < iz; ++i) {
|
|
stmt = body.body[i];
|
|
if (stmt.type !== 'DirectiveStatement') {
|
|
break;
|
|
}
|
|
if (stmt.raw === '"use strict"' || stmt.raw === '\'use strict\'') {
|
|
return true;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0, iz = body.body.length; i < iz; ++i) {
|
|
stmt = body.body[i];
|
|
if (stmt.type !== Syntax.ExpressionStatement) {
|
|
break;
|
|
}
|
|
expr = stmt.expression;
|
|
if (expr.type !== Syntax.Literal || typeof expr.value !== 'string') {
|
|
break;
|
|
}
|
|
if (expr.raw != null) {
|
|
if (expr.raw === '"use strict"' || expr.raw === '\'use strict\'') {
|
|
return true;
|
|
}
|
|
} else {
|
|
if (expr.value === 'use strict') {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function Scope(block, opt) {
|
|
var variable, body;
|
|
|
|
this.type =
|
|
(block.type === Syntax.CatchClause) ? 'catch' :
|
|
(block.type === Syntax.WithStatement) ? 'with' :
|
|
(block.type === Syntax.Program) ? 'global' : 'function';
|
|
this.set = new Map();
|
|
this.taints = new Map();
|
|
this.dynamic = this.type === 'global' || this.type === 'with';
|
|
this.block = block;
|
|
this.through = [];
|
|
this.variables = [];
|
|
this.references = [];
|
|
this.left = [];
|
|
this.variableScope =
|
|
(this.type === 'global' || this.type === 'function') ? this : currentScope.variableScope;
|
|
this.functionExpressionScope = false;
|
|
this.directCallToEvalScope = false;
|
|
this.thisFound = false;
|
|
body = this.type === 'function' ? block.body : block;
|
|
if (opt.naming) {
|
|
this.__define(block.id, {
|
|
type: Variable.FunctionName,
|
|
name: block.id,
|
|
node: block
|
|
});
|
|
this.functionExpressionScope = true;
|
|
} else {
|
|
if (this.type === 'function') {
|
|
variable = new Variable('arguments', this);
|
|
this.taints.set('arguments', true);
|
|
this.set.set('arguments', variable);
|
|
this.variables.push(variable);
|
|
}
|
|
|
|
if (block.type === Syntax.FunctionExpression && block.id) {
|
|
new Scope(block, { naming: true });
|
|
}
|
|
}
|
|
|
|
this.upper = currentScope;
|
|
this.isStrict = isStrictScope(this, block);
|
|
|
|
this.childScopes = [];
|
|
if (currentScope) {
|
|
currentScope.childScopes.push(this);
|
|
}
|
|
|
|
|
|
// RAII
|
|
currentScope = this;
|
|
if (this.type === 'global') {
|
|
globalScope = this;
|
|
}
|
|
scopes.push(this);
|
|
}
|
|
|
|
Scope.prototype.__close = function __close() {
|
|
var i, iz, ref, current, node;
|
|
|
|
// Because if this is global environment, upper is null
|
|
if (!this.dynamic || options.optimistic) {
|
|
// static resolve
|
|
for (i = 0, iz = this.left.length; i < iz; ++i) {
|
|
ref = this.left[i];
|
|
if (!this.__resolve(ref)) {
|
|
this.__delegateToUpperScope(ref);
|
|
}
|
|
}
|
|
} else {
|
|
// this is "global" / "with" / "function with eval" environment
|
|
if (this.type === 'with') {
|
|
for (i = 0, iz = this.left.length; i < iz; ++i) {
|
|
ref = this.left[i];
|
|
ref.tainted = true;
|
|
this.__delegateToUpperScope(ref);
|
|
}
|
|
} else {
|
|
for (i = 0, iz = this.left.length; i < iz; ++i) {
|
|
// notify all names are through to global
|
|
ref = this.left[i];
|
|
current = this;
|
|
do {
|
|
current.through.push(ref);
|
|
current = current.upper;
|
|
} while (current);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (this.type === 'global') {
|
|
for (i = 0, iz = this.left.length; i < iz; ++i) {
|
|
// create an implicit global variable from assignment expression
|
|
ref = this.left[i];
|
|
if (ref.__maybeImplicitGlobal) {
|
|
node = ref.__maybeImplicitGlobal;
|
|
this.__define(node.left, {
|
|
type: Variable.ImplicitGlobalVariable,
|
|
name: node.left,
|
|
node: node
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
this.left = null;
|
|
currentScope = this.upper;
|
|
};
|
|
|
|
Scope.prototype.__resolve = function __resolve(ref) {
|
|
var variable, name;
|
|
name = ref.identifier.name;
|
|
if (this.set.has(name)) {
|
|
variable = this.set.get(name);
|
|
variable.references.push(ref);
|
|
variable.stack = variable.stack && ref.from.variableScope === this.variableScope;
|
|
if (ref.tainted) {
|
|
variable.tainted = true;
|
|
this.taints.set(variable.name, true);
|
|
}
|
|
ref.resolved = variable;
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
Scope.prototype.__delegateToUpperScope = function __delegateToUpperScope(ref) {
|
|
if (this.upper) {
|
|
this.upper.left.push(ref);
|
|
}
|
|
this.through.push(ref);
|
|
};
|
|
|
|
Scope.prototype.__define = function __define(node, info) {
|
|
var name, variable;
|
|
if (node && node.type === Syntax.Identifier) {
|
|
name = node.name;
|
|
if (!this.set.has(name)) {
|
|
variable = new Variable(name, this);
|
|
variable.identifiers.push(node);
|
|
variable.defs.push(info);
|
|
this.set.set(name, variable);
|
|
this.variables.push(variable);
|
|
} else {
|
|
variable = this.set.get(name);
|
|
variable.identifiers.push(node);
|
|
variable.defs.push(info);
|
|
}
|
|
}
|
|
};
|
|
|
|
Scope.prototype.__referencing = function __referencing(node, assign, writeExpr, maybeImplicitGlobal) {
|
|
var ref;
|
|
// because Array element may be null
|
|
if (node && node.type === Syntax.Identifier) {
|
|
ref = new Reference(node, this, assign || Reference.READ, writeExpr, maybeImplicitGlobal);
|
|
this.references.push(ref);
|
|
this.left.push(ref);
|
|
}
|
|
};
|
|
|
|
Scope.prototype.__detectEval = function __detectEval() {
|
|
var current;
|
|
current = this;
|
|
this.directCallToEvalScope = true;
|
|
do {
|
|
current.dynamic = true;
|
|
current = current.upper;
|
|
} while (current);
|
|
};
|
|
|
|
Scope.prototype.__detectThis = function __detectThis() {
|
|
this.thisFound = true;
|
|
};
|
|
|
|
Scope.prototype.__isClosed = function isClosed() {
|
|
return this.left === null;
|
|
};
|
|
|
|
// API Scope#resolve(name)
|
|
// returns resolved reference
|
|
Scope.prototype.resolve = function resolve(ident) {
|
|
var ref, i, iz;
|
|
assert(this.__isClosed(), 'scope should be closed');
|
|
assert(ident.type === Syntax.Identifier, 'target should be identifier');
|
|
for (i = 0, iz = this.references.length; i < iz; ++i) {
|
|
ref = this.references[i];
|
|
if (ref.identifier === ident) {
|
|
return ref;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// API Scope#isStatic
|
|
// returns this scope is static
|
|
Scope.prototype.isStatic = function isStatic() {
|
|
return !this.dynamic;
|
|
};
|
|
|
|
// API Scope#isArgumentsMaterialized
|
|
// return this scope has materialized arguments
|
|
Scope.prototype.isArgumentsMaterialized = function isArgumentsMaterialized() {
|
|
// TODO(Constellation)
|
|
// We can more aggressive on this condition like this.
|
|
//
|
|
// function t() {
|
|
// // arguments of t is always hidden.
|
|
// function arguments() {
|
|
// }
|
|
// }
|
|
var variable;
|
|
|
|
// This is not function scope
|
|
if (this.type !== 'function') {
|
|
return true;
|
|
}
|
|
|
|
if (!this.isStatic()) {
|
|
return true;
|
|
}
|
|
|
|
variable = this.set.get('arguments');
|
|
assert(variable, 'always have arguments variable');
|
|
return variable.tainted || variable.references.length !== 0;
|
|
};
|
|
|
|
// API Scope#isThisMaterialized
|
|
// return this scope has materialized `this` reference
|
|
Scope.prototype.isThisMaterialized = function isThisMaterialized() {
|
|
// This is not function scope
|
|
if (this.type !== 'function') {
|
|
return true;
|
|
}
|
|
if (!this.isStatic()) {
|
|
return true;
|
|
}
|
|
return this.thisFound;
|
|
};
|
|
|
|
Scope.mangledName = '__$escope$__';
|
|
|
|
Scope.prototype.attach = function attach() {
|
|
if (!this.functionExpressionScope) {
|
|
this.block[Scope.mangledName] = this;
|
|
}
|
|
};
|
|
|
|
Scope.prototype.detach = function detach() {
|
|
if (!this.functionExpressionScope) {
|
|
delete this.block[Scope.mangledName];
|
|
}
|
|
};
|
|
|
|
Scope.prototype.isUsedName = function (name) {
|
|
if (this.set.has(name)) {
|
|
return true;
|
|
}
|
|
for (var i = 0, iz = this.through.length; i < iz; ++i) {
|
|
if (this.through[i].identifier.name === name) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
function ScopeManager(scopes) {
|
|
this.scopes = scopes;
|
|
this.attached = false;
|
|
}
|
|
|
|
// Returns appropliate scope for this node
|
|
ScopeManager.prototype.__get = function __get(node) {
|
|
var i, iz, scope;
|
|
if (this.attached) {
|
|
return node[Scope.mangledName] || null;
|
|
}
|
|
if (Scope.isScopeRequired(node)) {
|
|
for (i = 0, iz = this.scopes.length; i < iz; ++i) {
|
|
scope = this.scopes[i];
|
|
if (!scope.functionExpressionScope) {
|
|
if (scope.block === node) {
|
|
return scope;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
ScopeManager.prototype.acquire = function acquire(node) {
|
|
return this.__get(node);
|
|
};
|
|
|
|
ScopeManager.prototype.release = function release(node) {
|
|
var scope = this.__get(node);
|
|
if (scope) {
|
|
scope = scope.upper;
|
|
while (scope) {
|
|
if (!scope.functionExpressionScope) {
|
|
return scope;
|
|
}
|
|
scope = scope.upper;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
ScopeManager.prototype.attach = function attach() {
|
|
var i, iz;
|
|
for (i = 0, iz = this.scopes.length; i < iz; ++i) {
|
|
this.scopes[i].attach();
|
|
}
|
|
this.attached = true;
|
|
};
|
|
|
|
ScopeManager.prototype.detach = function detach() {
|
|
var i, iz;
|
|
for (i = 0, iz = this.scopes.length; i < iz; ++i) {
|
|
this.scopes[i].detach();
|
|
}
|
|
this.attached = false;
|
|
};
|
|
|
|
Scope.isScopeRequired = function isScopeRequired(node) {
|
|
return Scope.isVariableScopeRequired(node) || node.type === Syntax.WithStatement || node.type === Syntax.CatchClause;
|
|
};
|
|
|
|
Scope.isVariableScopeRequired = function isVariableScopeRequired(node) {
|
|
return node.type === Syntax.Program || node.type === Syntax.FunctionExpression || node.type === Syntax.FunctionDeclaration;
|
|
};
|
|
|
|
function analyze(tree, providedOptions) {
|
|
var resultScopes;
|
|
|
|
options = updateDeeply(defaultOptions(), providedOptions);
|
|
resultScopes = scopes = [];
|
|
currentScope = null;
|
|
globalScope = null;
|
|
|
|
// attach scope and collect / resolve names
|
|
estraverse.traverse(tree, {
|
|
enter: function enter(node) {
|
|
var i, iz, decl;
|
|
if (Scope.isScopeRequired(node)) {
|
|
new Scope(node, {});
|
|
}
|
|
|
|
switch (node.type) {
|
|
case Syntax.AssignmentExpression:
|
|
if (node.operator === '=') {
|
|
currentScope.__referencing(node.left, Reference.WRITE, node.right, (!currentScope.isStrict && node.left.name != null) && node);
|
|
} else {
|
|
currentScope.__referencing(node.left, Reference.RW, node.right);
|
|
}
|
|
currentScope.__referencing(node.right);
|
|
break;
|
|
|
|
case Syntax.ArrayExpression:
|
|
for (i = 0, iz = node.elements.length; i < iz; ++i) {
|
|
currentScope.__referencing(node.elements[i]);
|
|
}
|
|
break;
|
|
|
|
case Syntax.BlockStatement:
|
|
break;
|
|
|
|
case Syntax.BinaryExpression:
|
|
currentScope.__referencing(node.left);
|
|
currentScope.__referencing(node.right);
|
|
break;
|
|
|
|
case Syntax.BreakStatement:
|
|
break;
|
|
|
|
case Syntax.CallExpression:
|
|
currentScope.__referencing(node.callee);
|
|
for (i = 0, iz = node['arguments'].length; i < iz; ++i) {
|
|
currentScope.__referencing(node['arguments'][i]);
|
|
}
|
|
|
|
// check this is direct call to eval
|
|
if (!options.ignoreEval && node.callee.type === Syntax.Identifier && node.callee.name === 'eval') {
|
|
currentScope.variableScope.__detectEval();
|
|
}
|
|
break;
|
|
|
|
case Syntax.CatchClause:
|
|
currentScope.__define(node.param, {
|
|
type: Variable.CatchClause,
|
|
name: node.param,
|
|
node: node
|
|
});
|
|
break;
|
|
|
|
case Syntax.ConditionalExpression:
|
|
currentScope.__referencing(node.test);
|
|
currentScope.__referencing(node.consequent);
|
|
currentScope.__referencing(node.alternate);
|
|
break;
|
|
|
|
case Syntax.ContinueStatement:
|
|
break;
|
|
|
|
case Syntax.DirectiveStatement:
|
|
break;
|
|
|
|
case Syntax.DoWhileStatement:
|
|
currentScope.__referencing(node.test);
|
|
break;
|
|
|
|
case Syntax.DebuggerStatement:
|
|
break;
|
|
|
|
case Syntax.EmptyStatement:
|
|
break;
|
|
|
|
case Syntax.ExpressionStatement:
|
|
currentScope.__referencing(node.expression);
|
|
break;
|
|
|
|
case Syntax.ForStatement:
|
|
currentScope.__referencing(node.init);
|
|
currentScope.__referencing(node.test);
|
|
currentScope.__referencing(node.update);
|
|
break;
|
|
|
|
case Syntax.ForInStatement:
|
|
if (node.left.type === Syntax.VariableDeclaration) {
|
|
currentScope.__referencing(node.left.declarations[0].id, Reference.WRITE, null, false);
|
|
} else {
|
|
currentScope.__referencing(node.left, Reference.WRITE, null, (!currentScope.isStrict && node.left.name != null) && node);
|
|
}
|
|
currentScope.__referencing(node.right);
|
|
break;
|
|
|
|
case Syntax.FunctionDeclaration:
|
|
// FunctionDeclaration name is defined in upper scope
|
|
currentScope.upper.__define(node.id, {
|
|
type: Variable.FunctionName,
|
|
name: node.id,
|
|
node: node
|
|
});
|
|
for (i = 0, iz = node.params.length; i < iz; ++i) {
|
|
currentScope.__define(node.params[i], {
|
|
type: Variable.Parameter,
|
|
name: node.params[i],
|
|
node: node,
|
|
index: i
|
|
});
|
|
}
|
|
break;
|
|
|
|
case Syntax.FunctionExpression:
|
|
// id is defined in upper scope
|
|
for (i = 0, iz = node.params.length; i < iz; ++i) {
|
|
currentScope.__define(node.params[i], {
|
|
type: Variable.Parameter,
|
|
name: node.params[i],
|
|
node: node,
|
|
index: i
|
|
});
|
|
}
|
|
break;
|
|
|
|
case Syntax.Identifier:
|
|
break;
|
|
|
|
case Syntax.IfStatement:
|
|
currentScope.__referencing(node.test);
|
|
break;
|
|
|
|
case Syntax.Literal:
|
|
break;
|
|
|
|
case Syntax.LabeledStatement:
|
|
break;
|
|
|
|
case Syntax.LogicalExpression:
|
|
currentScope.__referencing(node.left);
|
|
currentScope.__referencing(node.right);
|
|
break;
|
|
|
|
case Syntax.MemberExpression:
|
|
currentScope.__referencing(node.object);
|
|
if (node.computed) {
|
|
currentScope.__referencing(node.property);
|
|
}
|
|
break;
|
|
|
|
case Syntax.NewExpression:
|
|
currentScope.__referencing(node.callee);
|
|
for (i = 0, iz = node['arguments'].length; i < iz; ++i) {
|
|
currentScope.__referencing(node['arguments'][i]);
|
|
}
|
|
break;
|
|
|
|
case Syntax.ObjectExpression:
|
|
break;
|
|
|
|
case Syntax.Program:
|
|
break;
|
|
|
|
case Syntax.Property:
|
|
currentScope.__referencing(node.value);
|
|
break;
|
|
|
|
case Syntax.ReturnStatement:
|
|
currentScope.__referencing(node.argument);
|
|
break;
|
|
|
|
case Syntax.SequenceExpression:
|
|
for (i = 0, iz = node.expressions.length; i < iz; ++i) {
|
|
currentScope.__referencing(node.expressions[i]);
|
|
}
|
|
break;
|
|
|
|
case Syntax.SwitchStatement:
|
|
currentScope.__referencing(node.discriminant);
|
|
break;
|
|
|
|
case Syntax.SwitchCase:
|
|
currentScope.__referencing(node.test);
|
|
break;
|
|
|
|
case Syntax.ThisExpression:
|
|
currentScope.variableScope.__detectThis();
|
|
break;
|
|
|
|
case Syntax.ThrowStatement:
|
|
currentScope.__referencing(node.argument);
|
|
break;
|
|
|
|
case Syntax.TryStatement:
|
|
break;
|
|
|
|
case Syntax.UnaryExpression:
|
|
currentScope.__referencing(node.argument);
|
|
break;
|
|
|
|
case Syntax.UpdateExpression:
|
|
currentScope.__referencing(node.argument, Reference.RW, null);
|
|
break;
|
|
|
|
case Syntax.VariableDeclaration:
|
|
for (i = 0, iz = node.declarations.length; i < iz; ++i) {
|
|
decl = node.declarations[i];
|
|
currentScope.variableScope.__define(decl.id, {
|
|
type: Variable.Variable,
|
|
name: decl.id,
|
|
node: decl,
|
|
index: i,
|
|
parent: node
|
|
});
|
|
if (decl.init) {
|
|
// initializer is found
|
|
currentScope.__referencing(decl.id, Reference.WRITE, decl.init, false);
|
|
currentScope.__referencing(decl.init);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Syntax.VariableDeclarator:
|
|
break;
|
|
|
|
case Syntax.WhileStatement:
|
|
currentScope.__referencing(node.test);
|
|
break;
|
|
|
|
case Syntax.WithStatement:
|
|
// WithStatement object is referenced at upper scope
|
|
currentScope.upper.__referencing(node.object);
|
|
break;
|
|
}
|
|
},
|
|
|
|
leave: function leave(node) {
|
|
while (currentScope && node === currentScope.block) {
|
|
currentScope.__close();
|
|
}
|
|
}
|
|
});
|
|
|
|
assert(currentScope === null);
|
|
globalScope = null;
|
|
scopes = null;
|
|
options = null;
|
|
|
|
return new ScopeManager(resultScopes);
|
|
}
|
|
|
|
exports.version = '0.0.16';
|
|
exports.Reference = Reference;
|
|
exports.Variable = Variable;
|
|
exports.Scope = Scope;
|
|
exports.ScopeManager = ScopeManager;
|
|
exports.analyze = analyze;
|
|
}, this));
|
|
/* vim: set sw=4 ts=4 et tw=80 : */
|