separate binding logic from scope to a binding class, move binding type resolution to the path so it can be used on any expression - #653

This commit is contained in:
Sebastian McKenzie 2015-03-13 01:08:46 +11:00
parent 40a111abbf
commit b44ba25d11
12 changed files with 372 additions and 225 deletions

View File

@ -0,0 +1 @@
for (var KEY = 0; KEY < ARR.length; KEY++) BODY;

View File

@ -432,7 +432,7 @@ class DestructuringTransformer {
} else { } else {
arrayRef = this.scope.generateUidBasedOnNode(arrayRef); arrayRef = this.scope.generateUidBasedOnNode(arrayRef);
this.nodes.push(this.buildVariableDeclaration(arrayRef, toArray)); this.nodes.push(this.buildVariableDeclaration(arrayRef, toArray));
this.scope.assignTypeGeneric(arrayRef.name, "Array"); this.getBinding(arrayRef.name).assignTypeGeneric("Array");
} }
// //

View File

@ -5,6 +5,12 @@ import * as t from "../../../types";
export var check = t.isForOfStatement; export var check = t.isForOfStatement;
export function ForOfStatement(node, parent, scope, file) { export function ForOfStatement(node, parent, scope, file) {
if (this.get("right").isTypeGeneric("Array")) {
return array(node, scope, file);
}
//
var callback = spec; var callback = spec;
if (file.isLoose("es6.forOf")) callback = loose; if (file.isLoose("es6.forOf")) callback = loose;
@ -36,6 +42,44 @@ export function ForOfStatement(node, parent, scope, file) {
} }
} }
var array = function (node, scope, file) {
var nodes = [];
var right = node.right;
if (!t.isIdentifier(right) || !scope.hasBinding(right.name)) {
var uid = scope.generateUidIdentifier("arr");
nodes.push(t.variableDeclaration("var", [
t.variableDeclarator(uid, right)
]));
right = uid;
}
var iterationKey = scope.generateUidIdentifier("i");
var loop = util.template("for-of-array", {
BODY: node.body,
KEY: iterationKey,
ARR: right
});
t.inherits(loop, node);
t.ensureBlock(loop);
var iterationValue = t.memberExpression(right, iterationKey, true);
var left = node.left;
if (t.isVariableDeclaration(left)) {
left.declarations[0].init = iterationValue;
loop.body.body.unshift(left);
} else {
loop.body.body.unshift(t.expressionStatement(t.assignmentExpression("=", left, iterationValue)));
}
nodes.push(loop);
return nodes;
};
var loose = function (node, parent, scope, file) { var loose = function (node, parent, scope, file) {
var left = node.left; var left = node.left;
var declar, id; var declar, id;

View File

@ -127,8 +127,6 @@ exports.Function = function (node, parent, scope, file) {
); );
} }
scope.assignTypeGeneric(rest.name, "Array");
var loop = util.template("rest", { var loop = util.template("rest", {
ARGUMENTS: argsId, ARGUMENTS: argsId,
ARRAY_KEY: arrKey, ARRAY_KEY: arrKey,
@ -136,7 +134,7 @@ exports.Function = function (node, parent, scope, file) {
START: start, START: start,
ARRAY: rest, ARRAY: rest,
KEY: key, KEY: key,
LEN: len, LEN: len
}); });
loop._blockHoist = node.params.length + 1; loop._blockHoist = node.params.length + 1;
node.body.body.unshift(loop); node.body.body.unshift(loop);

View File

@ -142,7 +142,7 @@ class TailCallTransformer {
hasDeopt() { hasDeopt() {
// check if the ownerId has been reassigned, if it has then it's not safe to // check if the ownerId has been reassigned, if it has then it's not safe to
// perform optimisations // perform optimisations
var ownerIdInfo = this.scope.getBindingInfo(this.ownerId.name); var ownerIdInfo = this.scope.getBinding(this.ownerId.name);
return ownerIdInfo && ownerIdInfo.reassigned; return ownerIdInfo && ownerIdInfo.reassigned;
} }

View File

@ -20,7 +20,6 @@ export default {
"playground.objectGetterMemoization": require("./playground/object-getter-memoization"), "playground.objectGetterMemoization": require("./playground/object-getter-memoization"),
reactCompat: require("./other/react-compat"), reactCompat: require("./other/react-compat"),
flow: require("./other/flow"),
react: require("./other/react"), react: require("./other/react"),
_modules: require("./internal/modules"), _modules: require("./internal/modules"),
@ -110,5 +109,6 @@ export default {
"utility.inlineExpressions": require("./utility/inline-expressions"), "utility.inlineExpressions": require("./utility/inline-expressions"),
"utility.deadCodeElimination": require("./utility/dead-code-elimination"), "utility.deadCodeElimination": require("./utility/dead-code-elimination"),
flow: require("./other/flow"),
_cleanUp: require("./internal/cleanup") _cleanUp: require("./internal/cleanup")
}; };

View File

@ -0,0 +1,92 @@
import * as t from "../types";
export default class Binding {
constructor({ identifier, scope, path, kind }) {
this.identifier = identifier;
this.reassigned = false;
this.scope = scope;
this.path = path;
this.kind = kind;
}
/**
* Description
*/
setTypeAnnotation() {
var typeInfo = this.path.getTypeAnnotation();
this.typeAnnotationInferred = typeInfo.inferred;
this.typeAnnotation = typeInfo.annotation;
}
/**
* Description
*/
isTypeGeneric(): boolean {
return this.path.isTypeGeneric(...arguments);
}
/**
* Description
*/
assignTypeGeneric(type: Object, params?) {
var typeParams = null;
if (params) params = t.typeParameterInstantiation(params);
this.assignType(t.genericTypeAnnotation(t.identifier(type), typeParams));
}
/**
* Description
*/
assignType(type: Object) {
this.typeAnnotation = type;
}
/**
* Description
*/
reassign() {
this.reassigned = true;
if (this.typeAnnotationInferred) {
// destroy the inferred typeAnnotation
this.typeAnnotation = null;
}
}
/**
* Description
*/
getValueIfImmutable() {
// can't guarantee this value is the same
if (this.reassigned) return;
var node = this.path.node;
if (t.isVariableDeclarator(node)) {
if (t.isIdentifier(node.id)) {
node = node.init;
} else {
// otherwise it's probably a destructuring like:
// var { foo } = "foo";
return;
}
}
if (t.isImmutable(node)) {
return node;
}
}
/**
* Description
*/
isCompatibleWithType(newType): boolean {
return false;
}
}

View File

@ -42,7 +42,6 @@ traverse.node = function (node, opts, scope, state, parentPath) {
function clearNode(node) { function clearNode(node) {
node._declarations = null; node._declarations = null;
node.extendedRange = null; node.extendedRange = null;
node._scopeInfo = null;
node._paths = null; node._paths = null;
node.tokens = null; node.tokens = null;
node.range = null; node.range = null;

View File

@ -1,3 +1,7 @@
import isBoolean from "lodash/lang/isBoolean";
import isNumber from "lodash/lang/isNumber";
import isRegExp from "lodash/lang/isRegExp";
import isString from "lodash/lang/isString";
import traverse from "./index"; import traverse from "./index";
import includes from "lodash/collection/includes"; import includes from "lodash/collection/includes";
import Scope from "./scope"; import Scope from "./scope";
@ -200,7 +204,104 @@ export default class TraversalPath {
} }
get(key) { get(key) {
return TraversalPath.get(this, this.context, this.node, this.node, key); var node = this.node;
var container = node[key];
if (Array.isArray(container)) {
return container.map((_, i) => {
return TraversalPath.get(this, this.context, node, container, i);
});
} else {
return TraversalPath.get(this, this.context, node, node, key);
}
}
has(key) {
return !!this.node[key];
}
getTypeAnnotation(): Object {
if (this.typeInfo) {
return this.typeInfo;
}
var info = this.typeInfo = {
inferred: false,
annotation: null
};
var type = this.node.typeAnnotation;
if (!type) {
info.inferred = true;
type = this.inferType(this);
}
if (type) {
if (t.isTypeAnnotation(type)) type = type.typeAnnotation;
info.annotation = type;
}
return info;
}
resolve(): ?TraversalPath {
if (this.isVariableDeclarator()) {
return this.get("init").resolve();
} else if (this.isIdentifier()) {
var binding = this.scope.getBinding(this.node.name);
if (!binding) return;
if (binding.path === this) {
return this;
} else {
return binding.path.resolve();;
}
} else if (this.isMemberExpression()) {
var targetKey = t.toComputedKey(this.node);
if (!t.isLiteral(targetKey)) return;
var targetName = targetKey.value;
var target = this.get("object").resolve();
if (!target || !target.isObjectExpression()) return;
var props = target.get("properties");
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (!prop.isProperty()) continue;
var key = prop.get("key");
if (key.isIdentifier({ name: targetName }) || key.isLiteral({ value: targetName })) {
return prop.get("value");
}
}
} else {
return this;
}
}
inferType(path: TraversalPath) {
path = path.resolve();
if (!path) return;
if (path.isRestElement() || path.isArrayExpression()) {
return t.genericTypeAnnotation(t.identifier("Array"));
}
if (path.isObjectExpression()) {
return t.genericTypeAnnotation(t.identifier("Object"));
}
if (path.isLiteral()) {
var value = path.node.value;
if (isString(value)) return t.stringTypeAnnotation();
if (isNumber(value)) return t.numberTypeAnnotation();
if (isBoolean(value)) return t.booleanTypeAnnotation();
}
if (path.isCallExpression()) {
var callee = path.get("callee").resolve();
if (callee && callee.isFunction()) return callee.node.returnType;
}
} }
isScope() { isScope() {
@ -215,13 +316,40 @@ export default class TraversalPath {
return t.isReferenced(this.node, this.parent); return t.isReferenced(this.node, this.parent);
} }
isBlockScoped() {
return t.isBlockScoped(this.node);
}
isVar() {
return t.isVar(this.node);
}
isScope() { isScope() {
return t.isScope(this.node, this.parent); return t.isScope(this.node, this.parent);
} }
isTypeGeneric(genericName: string, hasTypeParameters?): boolean {
var type = this.getTypeAnnotation().annotation;
if (!type) return false;
if (!t.isGenericTypeAnnotation(type) || !t.isIdentifier(type.id, { name: genericName })) {
return false;
}
if (hasTypeParameters && !type.typeParameters) {
return false;
}
return true;
}
getBindingIdentifiers() { getBindingIdentifiers() {
return t.getBindingIdentifiers(this.node); return t.getBindingIdentifiers(this.node);
} }
traverse(opts, state) {
traverse(this.node, opts, this.scope, state);
}
} }
for (var i = 0; i < t.TYPES.length; i++) { for (var i = 0; i < t.TYPES.length; i++) {

View File

@ -2,6 +2,7 @@ import includes from "lodash/collection/includes";
import traverse from "./index"; import traverse from "./index";
import defaults from "lodash/object/defaults"; import defaults from "lodash/object/defaults";
import * as messages from "../messages"; import * as messages from "../messages";
import Binding from "./binding";
import globals from "globals"; import globals from "globals";
import flatten from "lodash/array/flatten"; import flatten from "lodash/array/flatten";
import extend from "lodash/object/extend"; import extend from "lodash/object/extend";
@ -12,27 +13,27 @@ import * as t from "../types";
var functionVariableVisitor = { var functionVariableVisitor = {
enter(node, parent, scope, state) { enter(node, parent, scope, state) {
if (t.isFor(node)) { if (t.isFor(node)) {
each(t.FOR_INIT_KEYS, function (key) { each(t.FOR_INIT_KEYS, (key) => {
var declar = node[key]; var declar = this.get(key);
if (t.isVar(declar)) state.scope.registerBinding("var", declar); if (declar.isVar()) state.scope.registerBinding("var", declar);
}); });
} }
// this block is a function so we'll stop since none of the variables // this block is a function so we'll stop since none of the variables
// declared within are accessible // declared within are accessible
if (t.isFunction(node)) return this.skip(); if (this.isFunction()) return this.skip();
// function identifier doesn't belong to this scope // function identifier doesn't belong to this scope
if (state.blockId && node === state.blockId) return; if (state.blockId && node === state.blockId) return;
// delegate block scope handling to the `blockVariableVisitor` // delegate block scope handling to the `blockVariableVisitor`
if (t.isBlockScoped(node)) return; if (this.isBlockScoped()) return;
// this will be hit again once we traverse into it after this iteration // this will be hit again once we traverse into it after this iteration
if (t.isExportDeclaration(node) && t.isDeclaration(node.declaration)) return; if (this.isExportDeclaration() && t.isDeclaration(node.declaration)) return;
// we've ran into a declaration! // we've ran into a declaration!
if (t.isDeclaration(node)) state.scope.registerDeclaration(node); if (this.isDeclaration()) state.scope.registerDeclaration(this);
} }
}; };
@ -42,16 +43,20 @@ var programReferenceVisitor = {
state.addGlobal(node); state.addGlobal(node);
} else if (t.isLabeledStatement(node)) { } else if (t.isLabeledStatement(node)) {
state.addGlobal(node); state.addGlobal(node);
} else if (t.isAssignmentExpression(node) || t.isUpdateExpression(node) || (t.isUnaryExpression(node) && node.operator === "delete")) { } else if (t.isAssignmentExpression(node)) {
scope.registerBindingReassignment(node); scope.registerBindingReassignment(this.get("left"), this.get("right"));
} else if (t.isUpdateExpression(node)) {
// TODO
} else if (t.isUnaryExpression(node) && node.operator === "delete") {
scope.registerBindingReassignment(this.get("left"), null);
} }
} }
}; };
var blockVariableVisitor = { var blockVariableVisitor = {
enter(node, parent, scope, state) { enter(node, parent, scope, state) {
if (t.isFunctionDeclaration(node) || t.isBlockScoped(node)) { if (this.isFunctionDeclaration() || this.isBlockScoped()) {
state.registerDeclaration(node); state.registerDeclaration(this);
} else if (t.isScope(node, parent)) { } else if (t.isScope(node, parent)) {
this.skip(); this.skip();
} }
@ -112,9 +117,7 @@ export default class Scope {
*/ */
generateUidIdentifier(name: string) { generateUidIdentifier(name: string) {
var id = t.identifier(this.generateUid(name)); return t.identifier(this.generateUid(name));
this.getFunctionParent().registerBinding("uid", id);
return id;
} }
/** /**
@ -129,7 +132,8 @@ export default class Scope {
do { do {
uid = this._generateUid(name, i); uid = this._generateUid(name, i);
i++; i++;
} while (this.hasBinding(uid) || this.hasGlobal(uid)); } while (this.hasBinding(uid) || this.hasGlobal(uid) || this.hasUid(uid));
this.uids[uid] = true;
return uid; return uid;
} }
@ -139,6 +143,19 @@ export default class Scope {
return `_${id}`; return `_${id}`;
} }
/**
* Description
*/
hasUid(name): boolean {
var scope = this;
do {
if (scope.uids[name]) return true;
scope = scope.parent;
} while (scope);
return false;
}
/* /*
* Description * Description
*/ */
@ -217,7 +234,7 @@ export default class Scope {
rename(oldName: string, newName: string) { rename(oldName: string, newName: string) {
newName ||= this.generateUidIdentifier(oldName).name; newName ||= this.generateUidIdentifier(oldName).name;
var info = this.getBindingInfo(oldName); var info = this.getBinding(oldName);
if (!info) return; if (!info) return;
var binding = info.identifier; var binding = info.identifier;
@ -246,102 +263,6 @@ export default class Scope {
binding.name = newName; binding.name = newName;
} }
/**
* Description
*/
inferType(node: Object) {
var target;
if (t.isVariableDeclarator(node)) {
target = node.init;
}
if (t.isArrayExpression(target)) {
return t.genericTypeAnnotation(t.identifier("Array"));
}
if (t.isObjectExpression(target)) {
return;
}
if (t.isLiteral(target)) {
return;
}
if (t.isCallExpression(target) && t.isIdentifier(target.callee)) {
var funcInfo = this.getBindingInfo(target.callee.name);
if (funcInfo) {
var funcNode = funcInfo.node;
return !funcInfo.reassigned && t.isFunction(funcNode) && node.returnType;
}
}
if (t.isIdentifier(target)) {
return;
}
}
/**
* Description
*/
isTypeGeneric(name: string, genericName: string) {
var info = this.getBindingInfo(name);
if (!info) return false;
var type = info.typeAnnotation;
return t.isGenericTypeAnnotation(type) && t.isIdentifier(type.id, { name: genericName });
}
/**
* Description
*/
assignTypeGeneric(name: string, type: Object) {
this.assignType(name, t.genericTypeAnnotation(t.identifier(type)));
}
/**
* Description
*/
assignType(name: string, type: Object) {
var info = this.getBindingInfo(name);
if (!info) return;
info.typeAnnotation = type;
}
/**
* Description
*/
getTypeAnnotation(id: Object, node: Object): Object {
var info = {
annotation: null,
inferred: false
};
var type;
if (id.typeAnnotation) {
type = id.typeAnnotation;
}
if (!type) {
info.inferred = true;
type = this.inferType(node);
}
if (type) {
if (t.isTypeAnnotation(type)) type = type.typeAnnotation;
info.annotation = type;
}
return info;
}
/** /**
* Description * Description
*/ */
@ -349,8 +270,9 @@ export default class Scope {
toArray(node: Object, i?: number) { toArray(node: Object, i?: number) {
var file = this.file; var file = this.file;
if (t.isIdentifier(node) && this.isTypeGeneric(node.name, "Array")) { if (t.isIdentifier(node)) {
return node; var binding = this.getBinding(node.name);
if (binding && binding.isTypeGeneric("Array")) return node;
} }
if (t.isArrayExpression(node)) { if (t.isArrayExpression(node)) {
@ -376,33 +298,21 @@ export default class Scope {
* Description * Description
*/ */
refreshDeclaration(node: Object) { registerDeclaration(path: TraversalPath) {
if (t.isBlockScoped(node)) { var node = path.node;
this.getBlockParent().registerDeclaration(node);
} else if (t.isVariableDeclaration(node, { kind: "var" })) {
this.getFunctionParent().registerDeclaration(node);
} else if (node === this.block) {
this.recrawl();
}
}
/**
* Description
*/
registerDeclaration(node: Object) {
if (t.isFunctionDeclaration(node)) { if (t.isFunctionDeclaration(node)) {
this.registerBinding("hoisted", node); this.registerBinding("hoisted", path);
} else if (t.isVariableDeclaration(node)) { } else if (t.isVariableDeclaration(node)) {
for (var i = 0; i < node.declarations.length; i++) { var declarations = path.get("declarations");
this.registerBinding(node.kind, node.declarations[i]); for (var i = 0; i < declarations.length; i++) {
this.registerBinding(node.kind, declarations[i]);
} }
} else if (t.isClassDeclaration(node)) { } else if (t.isClassDeclaration(node)) {
this.registerBinding("let", node); this.registerBinding("let", path);
} else if (t.isImportDeclaration(node) || t.isExportDeclaration(node)) { } else if (t.isImportDeclaration(node) || t.isExportDeclaration(node)) {
this.registerBinding("module", node); this.registerBinding("module", path);
} else { } else {
this.registerBinding("unknown", node); this.registerBinding("unknown", path);
} }
} }
@ -410,18 +320,16 @@ export default class Scope {
* Description * Description
*/ */
registerBindingReassignment(node: Object) { registerBindingReassignment(left: TraversalPath, right: TraversalPath) {
var ids = t.getBindingIdentifiers(node); var ids = left.getBindingIdentifiers();
for (var name in ids) { for (var name in ids) {
var info = this.getBindingInfo(name); var binding = this.getBinding(name);
if (info) { if (!binding) continue;
info.reassigned = true; if (right) {
var rightType = right.typeAnnotation;
if (info.typeAnnotationInferred) { if (rightType && binding.isCompatibleWithType(rightType)) continue;
// destroy the inferred typeAnnotation
info.typeAnnotation = null;
}
} }
binding.reassign();
} }
} }
@ -429,27 +337,22 @@ export default class Scope {
* Description * Description
*/ */
registerBinding(kind: string, node: Object) { registerBinding(kind: string, path: TraversalPath) {
if (!kind) throw new ReferenceError("no `kind`"); if (!kind) throw new ReferenceError("no `kind`");
var ids = t.getBindingIdentifiers(node); var ids = path.getBindingIdentifiers();
for (var name in ids) { for (var name in ids) {
var id = ids[name]; var id = ids[name];
this.checkBlockScopedCollisions(kind, name, id); this.checkBlockScopedCollisions(kind, name, id);
var typeInfo = this.getTypeAnnotation(id, node); this.bindings[name] = new Binding({
this.bindings[name] = {
typeAnnotationInferred: typeInfo.inferred,
typeAnnotation: typeInfo.annotation,
reassigned: false,
identifier: id, identifier: id,
scope: this, scope: this,
node: node, path: path,
kind: kind kind: kind
}; });
} }
} }
@ -489,91 +392,92 @@ export default class Scope {
*/ */
crawl() { crawl() {
var block = this.block; var path = this.path;
var i; var i;
// //
var info = this.path.getData("scopeInfo"); var info = path.getData("scopeInfo");
if (info) { if (info) {
extend(this, info); extend(this, info);
return; return;
} }
info = this.path.setData("scopeInfo", { info = path.setData("scopeInfo", {
bindings: object(), bindings: object(),
globals: object() globals: object(),
uids: object()
}); });
extend(this, info); extend(this, info);
// ForStatement - left, init // ForStatement - left, init
if (t.isLoop(block)) { if (path.isLoop()) {
for (i = 0; i < t.FOR_INIT_KEYS.length; i++) { for (i = 0; i < t.FOR_INIT_KEYS.length; i++) {
var node = block[t.FOR_INIT_KEYS[i]]; var node = path.get(t.FOR_INIT_KEYS[i]);
if (t.isBlockScoped(node)) this.registerBinding("let", node); if (node.isBlockScoped()) this.registerBinding("let", node);
} }
if (t.isBlockStatement(block.body)) { var body = path.get("body");
block = block.body; if (body.isBlockStatement()) path = path.get("body");
}
} }
// FunctionExpression - id // FunctionExpression - id
if (t.isFunctionExpression(block) && block.id) { if (path.isFunctionExpression() && path.has("id")) {
if (!t.isProperty(this.parentBlock, { method: true })) { if (!t.isProperty(path.parent, { method: true })) {
this.registerBinding("var", block.id); this.registerBinding("var", path.get("id"));
} }
} }
// Class // Class
if (t.isClass(block) && block.id) { if (path.isClass() && path.has("id")) {
this.registerBinding("var", block.id); this.registerBinding("var", path.get("id"));
} }
// Function - params, rest // Function - params, rest
if (t.isFunction(block)) { if (path.isFunction()) {
for (i = 0; i < block.params.length; i++) { var params = path.get("params");
this.registerBinding("param", block.params[i]); for (i = 0; i < params.length; i++) {
this.registerBinding("param", params[i]);
} }
this.traverse(block.body, blockVariableVisitor, this); this.traverse(path.get("body"), blockVariableVisitor, this);
} }
// Program, BlockStatement, Function - let variables // Program, BlockStatement, Function - let variables
if (t.isBlockStatement(block) || t.isProgram(block)) { if (path.isBlockStatement() || path.isProgram()) {
this.traverse(block, blockVariableVisitor, this); this.traverse(path.node, blockVariableVisitor, this);
} }
// CatchClause - param // CatchClause - param
if (t.isCatchClause(block)) { if (path.isCatchClause()) {
this.registerBinding("let", block.param); this.registerBinding("let", path.get("param"));
} }
// ComprehensionExpression - blocks // ComprehensionExpression - blocks
if (t.isComprehensionExpression(block)) { if (path.isComprehensionExpression()) {
this.registerBinding("let", block); this.registerBinding("let", path);
} }
// Program, Function - var variables // Program, Function - var variables
if (t.isProgram(block) || t.isFunction(block)) { if (path.isProgram() || path.isFunction()) {
this.traverse(block, functionVariableVisitor, { this.traverse(path.node, functionVariableVisitor, {
blockId: block.id, blockId: path.get("id").node,
scope: this scope: this
}); });
} }
// Program // Program
if (t.isProgram(block)) { if (path.isProgram()) {
this.traverse(block, programReferenceVisitor, this); this.traverse(path.node, programReferenceVisitor, this);
} }
} }
@ -674,7 +578,7 @@ export default class Scope {
* Description * Description
*/ */
getBindingInfo(name: string) { getBinding(name: string) {
var scope = this; var scope = this;
do { do {
@ -696,7 +600,7 @@ export default class Scope {
*/ */
getBindingIdentifier(name: string) { getBindingIdentifier(name: string) {
var info = this.getBindingInfo(name); var info = this.getBinding(name);
return info && info.identifier; return info && info.identifier;
} }
@ -722,29 +626,11 @@ export default class Scope {
*/ */
getImmutableBindingValue(name: string) { getImmutableBindingValue(name: string) {
return this._immutableBindingInfoToValue(this.getBindingInfo(name)); return this._immutableBindingInfoToValue(this.getBinding(name));
} }
_immutableBindingInfoToValue(info) { _immutableBindingInfoToValue(binding) {
if (!info) return; if (binding) return binding.getValueIfImmutable();
// can't guarantee this value is the same
if (info.reassigned) return;
var node = info.node;
if (t.isVariableDeclarator(node)) {
if (t.isIdentifier(node.id)) {
node = node.init;
} else {
// otherwise it's probably a destructuring like:
// var { foo } = "foo";
return;
}
}
if (t.isImmutable(node)) {
return node;
}
} }
/** /**
@ -789,7 +675,7 @@ export default class Scope {
*/ */
removeBinding(name: string) { removeBinding(name: string) {
var info = this.getBindingInfo(name); var info = this.getBinding(name);
if (info) info.scope.removeOwnBinding(name); if (info) info.scope.removeOwnBinding(name);
} }
} }

View File

@ -10,7 +10,7 @@ import * as t from "./index";
* Description * Description
*/ */
export function toComputedKey(node: Object, key: Object = node.key): Object { export function toComputedKey(node: Object, key: Object = node.key || node.property): Object {
if (!node.computed) { if (!node.computed) {
if (t.isIdentifier(key)) key = t.literal(key.name); if (t.isIdentifier(key)) key = t.literal(key.name);
} }

View File

@ -281,7 +281,6 @@ export function inheritsComments(child: Object, parent: Object): Object {
export function inherits(child: Object, parent: Object): Object { export function inherits(child: Object, parent: Object): Object {
child._declarations = parent._declarations; child._declarations = parent._declarations;
child._scopeInfo = parent._scopeInfo;
child.range = parent.range; child.range = parent.range;
child.start = parent.start; child.start = parent.start;
child.loc = parent.loc; child.loc = parent.loc;