From fa95f743f31a614635c4e93e16c23f7bdd8693e3 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Tue, 24 Aug 2010 22:19:53 -0400 Subject: [PATCH] Fixing Issue #643. Be a little bit safer about declaring block variables as close to the block scope as possible. --- lib/nodes.js | 8 ++++++-- lib/scope.js | 12 +++++++----- src/nodes.coffee | 4 ++-- src/scope.coffee | 12 +++++++----- test/test_comprehensions.coffee | 11 +++++++++++ 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/lib/nodes.js b/lib/nodes.js index 2671bc01..a60e8293 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -1598,10 +1598,14 @@ name = (this.name && this.name.compile(o)) || scope.freeVariable(); index = this.index && this.index.compile(o); if (name && !this.pattern && (range || !codeInBody)) { - scope.find(name); + scope.find(name, { + immediate: true + }); } if (index) { - scope.find(index); + scope.find(index, { + immediate: true + }); } if (!(topLevel)) { rvar = scope.freeVariable(); diff --git a/lib/scope.js b/lib/scope.js index d309d3ef..92245037 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -21,8 +21,8 @@ return this; }; Scope.root = null; - Scope.prototype.find = function(name) { - if (this.check(name)) { + Scope.prototype.find = function(name, options) { + if (this.check(name, options)) { return true; } this.variables[name] = 'var'; @@ -43,9 +43,11 @@ Scope.prototype.parameter = function(name) { return (this.variables[name] = 'param'); }; - Scope.prototype.check = function(name) { - if (Object.prototype.hasOwnProperty.call(this.variables, name)) { - return true; + Scope.prototype.check = function(name, options) { + var immediate; + immediate = Object.prototype.hasOwnProperty.call(this.variables, name); + if (immediate || (options && options.immediate)) { + return immediate; } return !!(this.parent && this.parent.check(name)); }; diff --git a/src/nodes.coffee b/src/nodes.coffee index 85dd83a0..2e4d717b 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1357,8 +1357,8 @@ exports.ForNode = class ForNode extends BaseNode scope = o.scope name = (@name and @name.compile(o)) or scope.freeVariable() index = @index and @index.compile o - scope.find name if name and not @pattern and (range or not codeInBody) - scope.find index if index + scope.find(name, immediate: yes) if name and not @pattern and (range or not codeInBody) + scope.find(index, immediate: yes) if index rvar = scope.freeVariable() unless topLevel ivar = if codeInBody then scope.freeVariable() else if range then name else index or scope.freeVariable() varPart = '' diff --git a/src/scope.coffee b/src/scope.coffee index e8d4557b..234a7d54 100644 --- a/src/scope.coffee +++ b/src/scope.coffee @@ -28,8 +28,8 @@ exports.Scope = class Scope # Look up a variable name in lexical scope, and declare it if it does not # already exist. - find: (name) -> - return true if @check name + find: (name, options) -> + return true if @check name, options @variables[name] = 'var' false @@ -44,9 +44,11 @@ exports.Scope = class Scope parameter: (name) -> @variables[name] = 'param' - # Just check to see if a variable has already been declared, without reserving. - check: (name) -> - return true if Object::hasOwnProperty.call @variables, name + # Just check to see if a variable has already been declared, without reserving, + # walks up to the root scope. + check: (name, options) -> + immediate = Object::hasOwnProperty.call @variables, name + return immediate if immediate or (options and options.immediate) !!(@parent and @parent.check(name)) # If we need to store an intermediate result, find an available name for a diff --git a/test/test_comprehensions.coffee b/test/test_comprehensions.coffee index c292e71c..787147c6 100644 --- a/test/test_comprehensions.coffee +++ b/test/test_comprehensions.coffee @@ -141,3 +141,14 @@ ok all.sort().join(' ') is 'Whiskers cream tabby' exxes = 'x' for [0...10] 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 \ No newline at end of file