rewrite constants transformer

This commit is contained in:
Sebastian McKenzie 2015-01-22 07:37:18 +11:00
parent 6959e60e2c
commit 870954c6be
2 changed files with 54 additions and 75 deletions

View File

@ -5,83 +5,38 @@ var t = require("../../../types");
var _ = require("lodash");
var visitor = {
enter: function (child, parent, scope, context, state) {
if (child._ignoreConstant) return;
if (t.isVariableDeclaration(child)) return;
enter: function (node, parent, scope, context, state) {
if (t.isDeclaration(node) || t.isAssignmentExpression(node)) {
var ids = t.getIds(node, true);
if (t.isVariableDeclarator(child) || t.isDeclaration(child) || t.isAssignmentExpression(child)) {
state.check(parent, state.getIds(child), scope);
for (var key in ids) {
var id = ids[key];
var constant = state.constants[key];
// no constant exists
if (!constant) continue;
// check if the assignment id matches the constant declaration id
// if it does then it was the id used to initially declare the
// constant so we can just ignore it
if (id === constant) continue;
throw state.file.errorWithNode(id, key + " is read-only");
}
} else if (t.isScope(node)) {
context.skip();
}
}
};
exports.Program =
exports.BlockStatement =
exports.ForInStatement =
exports.ForOfStatement =
exports.ForStatement = function (node, parent, scope, context, file) {
var hasConstants = false;
var constants = {};
/**
* Check the results of `util.getIds` as `names` generated from a
* node against it's parent.
*/
var check = function (parent, names, scope) {
for (var name in names) {
var nameNode = names[name];
if (!_.has(constants, name)) continue;
if (parent && t.isBlockStatement(parent) && parent !== constants[name]) continue;
if (scope) {
var defined = scope.get(name);
if (defined && defined === nameNode) continue;
}
throw file.errorWithNode(nameNode, name + " is read-only");
}
};
var getIds = function (node) {
return t.getIds(node, true, ["MemberExpression"]);
};
/**
* Collect all constants in this scope.
*/
_.each(node.body, function (child, parent) {
if (t.isExportDeclaration(child)) {
child = child.declaration;
}
if (t.isVariableDeclaration(child, { kind: "const" })) {
for (var i = 0; i < child.declarations.length; i++) {
var declar = child.declarations[i];
var ids = getIds(declar);
for (var name in ids) {
var nameNode = ids[name];
var names = {};
names[name] = nameNode;
check(parent, names);
constants[name] = parent;
hasConstants = true;
}
declar._ignoreConstant = true;
}
child._ignoreConstant = true;
child.kind = "let";
}
exports.Scope = function (node, parent, scope, context, file) {
traverse(node, visitor, scope, {
constants: scope.getAllOfKind("const"),
file: file
});
if (!hasConstants) return;
var state = { check: check, getIds: getIds };
traverse(node, visitor, scope, state);
};
exports.VariableDeclaration = function (node) {
if (node.kind === "const") node.kind = "let";
};

View File

@ -25,6 +25,7 @@ function Scope(block, parent, file) {
var info = this.getInfo();
this.references = info.references;
this.declarations = info.declarations;
this.declarationKinds = info.declarationKinds;
}
var vars = require("jshint/src/vars");
@ -187,10 +188,15 @@ Scope.prototype.getInfo = function () {
var info = block._scopeInfo = {};
var references = info.references = {};
var declarations = info.declarations = {};
var declarationKinds = info.declarationKinds = {};
var add = function (node, reference, throwOnDuplicate) {
self._add(node, references);
if (!reference) self._add(node, declarations, throwOnDuplicate);
if (!reference) {
self._add(node, declarations, throwOnDuplicate);
self._add(node, declarationKinds[node.kind] = declarationKinds[node.kind] || {});
}
};
if (parent && t.isBlockStatement(block) && t.isFor(parent.block)) {
@ -240,7 +246,7 @@ Scope.prototype.getInfo = function () {
// Function - params, rest
if (t.isFunction(block)) {
add(block.rest);
if (block.rest) add(block.rest);
_.each(block.params, function (param) {
add(param);
});
@ -290,6 +296,24 @@ Scope.prototype.add = function (node) {
scope._add(node, scope.references);
};
/**
* Walks the scope tree and gathers all declarations of `kind`.
*
* @returns {Object}
*/
Scope.prototype.getAllOfKind = function (kind) {
var ids = {};
var scope = this;
do {
_.defaults(ids, scope.declarationKinds[kind]);
scope = scope.parent;
} while (scope);
return ids;
};
/**
* Description
*