1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

Issue #897 ... fixed leaking direct-call-plucked comprehension variables, due to shared scope.

This commit is contained in:
Jeremy Ashkenas 2010-12-05 21:18:30 -05:00
parent c0bbc609be
commit 2decb30d4e
6 changed files with 41 additions and 23 deletions

View file

@ -56,7 +56,7 @@
return compileScripts(); return compileScripts();
}; };
compileScripts = function() { compileScripts = function() {
var base, compile, _fn, _i, _len, _results; var base, compile, source, _fn, _i, _len, _results;
_fn = function(source) { _fn = function(source) {
base = path.join(source); base = path.join(source);
compile = function(source, topLevel) { compile = function(source, topLevel) {

View file

@ -1204,9 +1204,12 @@
return !!this.ctor; return !!this.ctor;
}; };
Code.prototype.compileNode = function(o) { Code.prototype.compileNode = function(o) {
var code, exprs, i, idt, lit, p, param, ref, scope, sharedScope, splats, v, val, vars, wasEmpty, _i, _j, _k, _len, _len2, _len3, _len4, _ref, _ref2, _ref3, _ref4, _results; var code, exprs, i, idt, lit, p, param, ref, sharedScope, splats, v, val, vars, wasEmpty, _i, _j, _k, _len, _len2, _len3, _len4, _ref, _ref2, _ref3, _ref4, _results;
sharedScope = del(o, 'sharedScope'); sharedScope = del(o, 'sharedScope');
o.scope = scope = sharedScope || new Scope(o.scope, this.body, this); o.scope = sharedScope || new Scope(o.scope, this.body, this);
if (sharedScope) {
o.scope.shared = true;
}
o.indent += TAB; o.indent += TAB;
delete o.bare; delete o.bare;
delete o.globals; delete o.globals;
@ -1259,7 +1262,7 @@
if (!splats) { if (!splats) {
for (i = 0, _len4 = vars.length; i < _len4; i++) { for (i = 0, _len4 = vars.length; i < _len4; i++) {
v = vars[i]; v = vars[i];
scope.parameter(vars[i] = v.compile(o)); o.scope.parameter(vars[i] = v.compile(o));
} }
} }
if (!(wasEmpty || this.noReturn)) { if (!(wasEmpty || this.noReturn)) {
@ -1769,7 +1772,6 @@
scope = o.scope; scope = o.scope;
name = this.name && this.name.compile(o, LEVEL_LIST); name = this.name && this.name.compile(o, LEVEL_LIST);
index = this.index && this.index.compile(o, LEVEL_LIST); index = this.index && this.index.compile(o, LEVEL_LIST);
if (!hasCode) {
if (name && !this.pattern) { if (name && !this.pattern) {
scope.find(name, { scope.find(name, {
immediate: true immediate: true
@ -1780,7 +1782,6 @@
immediate: true immediate: true
}); });
} }
}
if (this.returns && !hasPure) { if (this.returns && !hasPure) {
rvar = scope.freeVariable('results'); rvar = scope.freeVariable('results');
} }

View file

@ -48,6 +48,9 @@
return false; return false;
}; };
Scope.prototype.parameter = function(name) { Scope.prototype.parameter = function(name) {
if (this.shared && this.check(name, true)) {
return;
}
return this.add(name, 'param'); return this.add(name, 'param');
}; };
Scope.prototype.check = function(name, immediate) { Scope.prototype.check = function(name, immediate) {

View file

@ -983,7 +983,8 @@ exports.Code = class Code extends Base
# a closure. # a closure.
compileNode: (o) -> compileNode: (o) ->
sharedScope = del o, 'sharedScope' sharedScope = del o, 'sharedScope'
o.scope = scope = sharedScope or new Scope o.scope, @body, this o.scope = sharedScope or new Scope o.scope, @body, this
o.scope.shared = yes if sharedScope
o.indent += TAB o.indent += TAB
delete o.bare delete o.bare
delete o.globals delete o.globals
@ -1008,7 +1009,7 @@ exports.Code = class Code extends Base
wasEmpty = @body.isEmpty() wasEmpty = @body.isEmpty()
exprs.unshift splats if splats exprs.unshift splats if splats
@body.expressions.unshift exprs... if exprs.length @body.expressions.unshift exprs... if exprs.length
scope.parameter vars[i] = v.compile o for v, i in vars unless splats o.scope.parameter vars[i] = v.compile o for v, i in vars unless splats
@body.makeReturn() unless wasEmpty or @noReturn @body.makeReturn() unless wasEmpty or @noReturn
idt = o.indent idt = o.indent
code = 'function' code = 'function'
@ -1425,7 +1426,6 @@ exports.For = class For extends Base
scope = o.scope scope = o.scope
name = @name and @name.compile o, LEVEL_LIST name = @name and @name.compile o, LEVEL_LIST
index = @index and @index.compile o, LEVEL_LIST index = @index and @index.compile o, LEVEL_LIST
unless hasCode
scope.find(name, immediate: yes) if name and not @pattern scope.find(name, immediate: yes) if name and not @pattern
scope.find(index, immediate: yes) if index scope.find(index, immediate: yes) if index
rvar = scope.freeVariable 'results' if @returns and not hasPure rvar = scope.freeVariable 'results' if @returns and not hasPure

View file

@ -44,6 +44,7 @@ exports.Scope = class Scope
# Reserve a variable name as originating from a function parameter for this # Reserve a variable name as originating from a function parameter for this
# scope. No `var` required for internal references. # scope. No `var` required for internal references.
parameter: (name) -> parameter: (name) ->
return if @shared and @check name, yes
@add name, 'param' @add 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,

View file

@ -228,3 +228,16 @@ foo = ->
-> i + j -> i + j
eq foo()[3][4](), 7 eq foo()[3][4](), 7
# Issue #897: Ensure that plucked function variables aren't leaked.
facets = {}
list = ['one', 'two']
(->
for entity in list
facets[entity] = -> entity
)()
eq typeof entity, 'undefined'
eq facets['two'](), 'two'