clean up scope API

This commit is contained in:
Sebastian McKenzie
2015-02-03 19:33:32 +11:00
parent 1801b725bd
commit a9405e5e80
11 changed files with 69 additions and 42 deletions

View File

@@ -407,7 +407,7 @@ File.prototype.generateUid = function (name, scope) {
do {
uid = this._generateUid(name, i);
i++;
} while (scope.has(uid));
} while (scope.hasReference(uid));
return uid;
};

View File

@@ -5,7 +5,7 @@ var t = require("../../types");
var getObjRef = function (node, nodes, file, scope) {
var ref;
if (t.isIdentifier(node)) {
if (scope.has(node.name, true)) {
if (scope.hasBinding(node.name)) {
// this variable is declared in scope so we can be 100% sure
// that evaluating it multiple times wont trigger a getter
// or something else
@@ -18,7 +18,7 @@ var getObjRef = function (node, nodes, file, scope) {
} else if (t.isMemberExpression(node)) {
ref = node.object;
if (t.isIdentifier(ref) && scope.has(ref.name)) {
if (t.isIdentifier(ref) && scope.hasReference(ref.name)) {
// the object reference that we need to save is locally declared
// so as per the previous comment we can be 100% sure evaluating
// it multiple times will be safe

View File

@@ -14,7 +14,7 @@ var visitor = {
// check that we don't have a local variable declared as that removes the need
// for the wrapper
var localDeclar = scope.get(state.id, true);
var localDeclar = scope.getBinding(state.id);
if (localDeclar !== state.outerDeclar) return;
state.selfReference = true;
@@ -32,7 +32,7 @@ exports.property = function (node, file, scope) {
var state = {
id: id,
selfReference: false,
outerDeclar: scope.get(id, true),
outerDeclar: scope.getBinding(id),
};
traverse(node, visitor, scope, state);

View File

@@ -147,7 +147,7 @@ DefaultFormatter.prototype.remapExportAssignment = function (node) {
DefaultFormatter.prototype.isLocalReference = function (node, scope) {
var localExports = this.localExports;
var name = node.name;
return t.isIdentifier(node) && localExports[name] && localExports[name] === scope.get(name, true);
return t.isIdentifier(node) && localExports[name] && localExports[name] === scope.getBinding(name);
};
DefaultFormatter.prototype.getModuleName = function () {

View File

@@ -11,7 +11,7 @@ var visitor = {
if (!declared) return;
// declared node is different in this scope
if (scope.get(node.name, true) !== declared) return;
if (scope.getBinding(node.name) !== declared) return;
var declaredLoc = declared.loc;
var referenceLoc = node.loc;

View File

@@ -110,7 +110,7 @@ function replace(node, parent, scope, context, remaps) {
var remap = remaps[node.name];
if (!remap) return;
var own = scope.get(node.name, true);
var own = scope.getBinding(node.name);
if (own === remap.node) {
node.name = remap.uid;
} else {
@@ -149,7 +149,7 @@ BlockScoping.prototype.remap = function () {
// this is the defining identifier of a declaration
var ref = letRefs[key];
if (scope.parentHas(key)) {
if (scope.parentHasReference(key)) {
var uid = scope.generateUidIdentifier(ref.name).name;
ref.name = uid;
@@ -227,7 +227,7 @@ var letReferenceFunctionVisitor = {
// this scope has a variable with the same name so it couldn't belong
// to our let scope
if (scope.hasOwn(node.name, true)) return;
if (scope.hasOwnBinding(node.name)) return;
// not a part of our scope
if (!state.letReferences[node.name]) return;

View File

@@ -21,7 +21,7 @@ var visitor = {
// constant so we can just ignore it
if (id === constant) continue;
var localBinding = scope.get(key, true);
var localBinding = scope.getBinding(key);
if (localBinding !== constant) continue;
throw state.file.errorWithNode(id, key + " is read-only");

View File

@@ -13,7 +13,7 @@ var hasDefaults = function (node) {
var iifeVisitor = {
enter: function (node, parent, scope, context, state) {
if (t.isReferencedIdentifier(node, parent) && state.scope.hasOwn(node.name)) {
if (t.isReferencedIdentifier(node, parent) && state.scope.hasOwnReference(node.name)) {
state.iife = true;
context.stop();
}
@@ -48,7 +48,7 @@ exports.Function = function (node, parent, scope) {
node.params[i] = scope.generateUidIdentifier("x");
if (!state.iife) {
if (t.isIdentifier(right) && scope.hasOwn(right.name)) {
if (t.isIdentifier(right) && scope.hasOwnReference(right.name)) {
state.iife = true;
} else {
traverse(right, iifeVisitor, scope, state);

View File

@@ -35,7 +35,7 @@ var astVisitor = {
context.skip();
return t.prependToMemberExpression(node, file.get("coreIdentifier"));
}
} else if (t.isReferencedIdentifier(node, parent) && !t.isMemberExpression(parent) && contains(ALIASABLE_CONSTRUCTORS, node.name) && !scope.get(node.name, true)) {
} else if (t.isReferencedIdentifier(node, parent) && !t.isMemberExpression(parent) && contains(ALIASABLE_CONSTRUCTORS, node.name) && !scope.getBinding(node.name)) {
// Symbol() -> _core.Symbol(); new Promise -> new _core.Promise
return t.memberExpression(file.get("coreIdentifier"), node);
} else if (t.isCallExpression(node)) {

View File

@@ -7,19 +7,19 @@ exports.optional = true;
exports.Identifier = function (node, parent, scope, context, file) {
if (!t.isReferenced(node, parent)) return;
if (scope.has(node.name, true)) return;
if (scope.hasBinding(node.name)) return;
var msg = "Reference to undeclared variable";
// get the closest declaration to offer as a suggestion
// the variable name may have just been mistyped
var declarations = scope.getAllDeclarations();
var bindings = scope.getAllBindings();
var closest;
var shortest = -1;
for (var name in declarations) {
for (var name in bindings) {
var distance = levenshtein(node.name, name);
if (distance <= 0 || distance > 3) continue;
if (distance <= shortest) continue;

View File

@@ -14,7 +14,7 @@ var has = require("lodash/object/has");
var t = require("../types");
/**
* This searches the current "scope" and collects all references/declarations
* This searches the current "scope" and collects all references/bindings
* within.
*
* @param {Node} block
@@ -31,8 +31,9 @@ function Scope(block, parentBlock, parent, file) {
this.block = block;
var info = this.getInfo();
this.references = info.references;
this.declarations = info.declarations;
this.references = info.references;
this.bindings = info.bindings;
this.declarationKinds = info.declarationKinds;
}
@@ -113,7 +114,7 @@ Scope.prototype.generateUidBasedOnNode = function (parent) {
*/
Scope.prototype.generateTempBasedOnNode = function (node) {
if (t.isIdentifier(node) && this.has(node.name, true)) {
if (t.isIdentifier(node) && this.hasBinding(node.name)) {
return null;
}
@@ -154,7 +155,7 @@ var functionVariableVisitor = {
var programReferenceVisitor = {
enter: function (node, parent, scope, context, add) {
if (t.isReferencedIdentifier(node, parent) && !scope.has(node.name)) {
if (t.isReferencedIdentifier(node, parent) && !scope.hasReference(node.name)) {
add(node, true);
}
}
@@ -177,8 +178,10 @@ Scope.prototype.getInfo = function () {
if (block._scopeInfo) return block._scopeInfo;
var info = block._scopeInfo = {};
var references = info.references = object();
var declarations = info.declarations = object();
var bindings = info.bindings = object();
var references = info.references = object();
var types = info.types = object();
var declarationKinds = info.declarationKinds = {
"var": object(),
"let": object(),
@@ -197,7 +200,7 @@ Scope.prototype.getInfo = function () {
}
}
extend(declarations, ids);
extend(bindings, ids);
var kinds = declarationKinds[node.kind];
if (kinds) extend(kinds, ids);
@@ -312,7 +315,7 @@ Scope.prototype.addDeclarationToFunctionScope = function (kind, node) {
var scope = this.getFunctionParent();
var ids = t.getBindingIdentifiers(node);
extend(scope.declarations, ids);
extend(scope.bindings, ids);
extend(scope.references, ids);
// this ignores the duplicate declaration logic specified in `getInfo`
@@ -334,17 +337,17 @@ Scope.prototype.getFunctionParent = function () {
};
/**
* Walks the scope tree and gathers **all** declarations.
* Walks the scope tree and gathers **all** bindings.
*
* @returns {Object}
*/
Scope.prototype.getAllDeclarations = function () {
Scope.prototype.getAllBindings = function () {
var ids = object();
var scope = this;
do {
defaults(ids, scope.declarations);
defaults(ids, scope.bindings);
scope = scope.parent;
} while (scope);
@@ -377,8 +380,8 @@ Scope.prototype.getAllDeclarationsOfKind = function (kind) {
* @param {Boolean} [decl]
*/
Scope.prototype.get = function (id, decl) {
return id && (this.getOwn(id, decl) || this.parentGet(id, decl));
Scope.prototype.getReference = function (id, decl) {
return id && (this.getOwnReference(id, decl) || this.parentGetReference(id, decl));
};
/**
@@ -388,9 +391,9 @@ Scope.prototype.get = function (id, decl) {
* @param {Boolean} [decl]
*/
Scope.prototype.getOwn = function (id, decl) {
Scope.prototype.getOwnReference = function (id, decl) {
var refs = this.references;
if (decl) refs = this.declarations;
if (decl) refs = this.bindings;
return has(refs, id) && refs[id];
};
@@ -401,8 +404,8 @@ Scope.prototype.getOwn = function (id, decl) {
* @param {Boolean} [decl]
*/
Scope.prototype.parentGet = function (id, decl) {
return this.parent && this.parent.get(id, decl);
Scope.prototype.parentGetReference = function (id, decl) {
return this.parent && this.parent.getReference(id, decl);
};
/**
@@ -413,10 +416,10 @@ Scope.prototype.parentGet = function (id, decl) {
* @returns {Boolean}
*/
Scope.prototype.has = function (id, decl) {
Scope.prototype.hasReference = function (id, decl) {
if (!id) return false;
if (this.hasOwn(id, decl)) return true;
if (this.parentHas(id, decl)) return true;
if (this.hasOwnReference(id, decl)) return true;
if (this.parentHasReference(id, decl)) return true;
if (contains(Scope.defaultDeclarations, id)) return true;
return false;
};
@@ -429,8 +432,8 @@ Scope.prototype.has = function (id, decl) {
* @returns {Boolean}
*/
Scope.prototype.hasOwn = function (id, decl) {
return !!this.getOwn(id, decl);
Scope.prototype.hasOwnReference = function (id, decl) {
return !!this.getOwnReference(id, decl);
};
/**
@@ -441,6 +444,30 @@ Scope.prototype.hasOwn = function (id, decl) {
* @returns {Boolean}
*/
Scope.prototype.parentHas = function (id, decl) {
return this.parent && this.parent.has(id, decl);
Scope.prototype.parentHasReference = function (id, decl) {
return this.parent && this.parent.hasReference(id, decl);
};
Scope.prototype.getBinding = function (id) {
return this.getReference(id, true);
};
Scope.prototype.hasBinding = function (id) {
return this.hasReference(id, true);
};
Scope.prototype.getOwnBinding = function (id) {
return this.getOwnReference(id, true);
};
Scope.prototype.hasOwnBinding = function (id) {
return this.hasOwnReference(id, true);
};
Scope.prototype.parentGetBinding = function (id) {
return this.parentGetReference(id, true);
};
Scope.prototype.parentHasBinding = function (id) {
return this.parentHasReference(id, true);
};