Fixing Issue #643. Be a little bit safer about declaring block variables as close to the block scope as possible.

This commit is contained in:
Jeremy Ashkenas 2010-08-24 22:19:53 -04:00
parent e2c46d14f0
commit fa95f743f3
5 changed files with 33 additions and 14 deletions

View File

@ -1598,10 +1598,14 @@
name = (this.name && this.name.compile(o)) || scope.freeVariable(); name = (this.name && this.name.compile(o)) || scope.freeVariable();
index = this.index && this.index.compile(o); index = this.index && this.index.compile(o);
if (name && !this.pattern && (range || !codeInBody)) { if (name && !this.pattern && (range || !codeInBody)) {
scope.find(name); scope.find(name, {
immediate: true
});
} }
if (index) { if (index) {
scope.find(index); scope.find(index, {
immediate: true
});
} }
if (!(topLevel)) { if (!(topLevel)) {
rvar = scope.freeVariable(); rvar = scope.freeVariable();

View File

@ -21,8 +21,8 @@
return this; return this;
}; };
Scope.root = null; Scope.root = null;
Scope.prototype.find = function(name) { Scope.prototype.find = function(name, options) {
if (this.check(name)) { if (this.check(name, options)) {
return true; return true;
} }
this.variables[name] = 'var'; this.variables[name] = 'var';
@ -43,9 +43,11 @@
Scope.prototype.parameter = function(name) { Scope.prototype.parameter = function(name) {
return (this.variables[name] = 'param'); return (this.variables[name] = 'param');
}; };
Scope.prototype.check = function(name) { Scope.prototype.check = function(name, options) {
if (Object.prototype.hasOwnProperty.call(this.variables, name)) { var immediate;
return true; immediate = Object.prototype.hasOwnProperty.call(this.variables, name);
if (immediate || (options && options.immediate)) {
return immediate;
} }
return !!(this.parent && this.parent.check(name)); return !!(this.parent && this.parent.check(name));
}; };

View File

@ -1357,8 +1357,8 @@ exports.ForNode = class ForNode extends BaseNode
scope = o.scope scope = o.scope
name = (@name and @name.compile(o)) or scope.freeVariable() name = (@name and @name.compile(o)) or scope.freeVariable()
index = @index and @index.compile o index = @index and @index.compile o
scope.find name if name and not @pattern and (range or not codeInBody) scope.find(name, immediate: yes) if name and not @pattern and (range or not codeInBody)
scope.find index if index scope.find(index, immediate: yes) if index
rvar = scope.freeVariable() unless topLevel rvar = scope.freeVariable() unless topLevel
ivar = if codeInBody then scope.freeVariable() else if range then name else index or scope.freeVariable() ivar = if codeInBody then scope.freeVariable() else if range then name else index or scope.freeVariable()
varPart = '' varPart = ''

View File

@ -28,8 +28,8 @@ exports.Scope = class Scope
# Look up a variable name in lexical scope, and declare it if it does not # Look up a variable name in lexical scope, and declare it if it does not
# already exist. # already exist.
find: (name) -> find: (name, options) ->
return true if @check name return true if @check name, options
@variables[name] = 'var' @variables[name] = 'var'
false false
@ -44,9 +44,11 @@ exports.Scope = class Scope
parameter: (name) -> parameter: (name) ->
@variables[name] = 'param' @variables[name] = 'param'
# Just check to see if a variable has already been declared, without reserving. # Just check to see if a variable has already been declared, without reserving,
check: (name) -> # walks up to the root scope.
return true if Object::hasOwnProperty.call @variables, name check: (name, options) ->
immediate = Object::hasOwnProperty.call @variables, name
return immediate if immediate or (options and options.immediate)
!!(@parent and @parent.check(name)) !!(@parent and @parent.check(name))
# If we need to store an intermediate result, find an available name for a # If we need to store an intermediate result, find an available name for a

View File

@ -141,3 +141,14 @@ ok all.sort().join(' ') is 'Whiskers cream tabby'
exxes = 'x' for [0...10] exxes = 'x' for [0...10]
ok exxes.join(' ') is 'x x x x x x x x x x' ok exxes.join(' ') is 'x x x x x x x x x x'
# Comprehensions safely redeclare parameters if they're not present in closest
# scope.
rule = (x) -> x
learn = ->
rule for rule in [1, 2, 3]
ok learn().join(' ') is '1 2 3'
ok rule(101) is 101