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:
parent
e2c46d14f0
commit
fa95f743f3
|
@ -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();
|
||||||
|
|
12
lib/scope.js
12
lib/scope.js
|
@ -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));
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 = ''
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue