+ Note how because we are assigning the value of the comprehensions to a
+ variable in the example above, CoffeeScript is collecting the result of
+ each iteration into an array. Sometimes functions end with loops that are
+ intended to run only for their side-effects. Be careful that you're not
+ accidentally returning the results of the comprehension in these cases,
+ by adding a meaningful return value, like true, or null,
+ to the bottom of your function.
+
Comprehensions can also be used to iterate over the keys and values in
an object. Use of to signal comprehension over the properties of
diff --git a/documentation/js/array_comprehensions.js b/documentation/js/array_comprehensions.js
index bbbe4c90..07f136c7 100644
--- a/documentation/js/array_comprehensions.js
+++ b/documentation/js/array_comprehensions.js
@@ -1,17 +1,6 @@
-var food, lunch, pos, roid, roid2, _i, _j, _len, _len2, _len3, _ref;
+var food, _i, _len, _ref;
_ref = ['toast', 'cheese', 'wine'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
food = _ref[_i];
- lunch = eat(food);
-}
-for (pos = 0, _len2 = asteroids.length; pos < _len2; pos++) {
- roid = asteroids[pos];
- for (_j = 0, _len3 = asteroids.length; _j < _len3; _j++) {
- roid2 = asteroids[_j];
- if (roid !== roid2) {
- if (roid.overlaps(roid2)) {
- roid.explode();
- }
- }
- }
+ eat(food);
}
\ No newline at end of file
diff --git a/documentation/js/splices.js b/documentation/js/splices.js
index ed47b9b2..a6f916f1 100644
--- a/documentation/js/splices.js
+++ b/documentation/js/splices.js
@@ -1,3 +1,3 @@
-var numbers, _ref;
+var numbers;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
-([].splice.apply(numbers, [3, 6 - 3 + 1].concat(_ref = [-3, -4, -5, -6])), _ref);
\ No newline at end of file
+[].splice.apply(numbers, [3, 4].concat([-3, -4, -5, -6]));
\ No newline at end of file
diff --git a/index.html b/index.html
index 75c421e0..42126459 100644
--- a/index.html
+++ b/index.html
@@ -854,28 +854,12 @@ lyrics = function() {
would use a loop, each/forEach, map, or select/filter.
# Eat lunch.
-lunch = eat food for food in ['toast', 'cheese', 'wine']
-
-# Naive collision detection.
-for roid, pos in asteroids
- for roid2 in asteroids when roid isnt roid2
- roid.explode() if roid.overlaps roid2
-
var food, lunch, pos, roid, roid2, _i, _j, _len, _len2, _len3, _ref;
+eat food for food in ['toast', 'cheese', 'wine']
+
+ Note how because we are assigning the value of the comprehensions to a
+ variable in the example above, CoffeeScript is collecting the result of
+ each iteration into an array. Sometimes functions end with loops that are
+ intended to run only for their side-effects. Be careful that you're not
+ accidentally returning the results of the comprehension in these cases,
+ by adding a meaningful return value, like true, or null,
+ to the bottom of your function.
+
Comprehensions can also be used to iterate over the keys and values in
an object. Use of to signal comprehension over the properties of
@@ -975,12 +968,12 @@ middle = copy.slice(3, 6 + 1);;alert(middle);'>run: middle apply(numbers, [3, 4].concat([-3, -4, -5, -6]));
+
+[].splice.apply(numbers, [3, 4].concat([-3, -4, -5, -6]));;alert(numbers);'>run: numbers
Note that JavaScript strings are immutable, and can't be spliced.
@@ -1920,7 +1913,7 @@ task('build:parser
js = ' ' if js.length <= 0
fs.writeFile jsPath, js, (err) ->
if err then printLine err.message
- else if opts.compile and opts.watch then printLine "Compiled #{source}"
+ else if opts.compile and opts.watch then utils.log "compiled #{source}"
path.exists dir, (exists) ->
if exists then compile() else exec "mkdir -p #{dir}", compile
From f6be426aa0fa67122dc98945442f3ce7f625973c Mon Sep 17 00:00:00 2001
From: Jeremy Ashkenas
Date: Sun, 5 Dec 2010 17:51:52 -0500
Subject: [PATCH 6/8] utils -> util.
---
lib/command.js | 6 +++---
src/command.coffee | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/command.js b/lib/command.js
index 08b25954..7172f651 100644
--- a/lib/command.js
+++ b/lib/command.js
@@ -1,8 +1,8 @@
(function() {
- var ALL_SWITCHES, BANNER, CoffeeScript, DEPRECATED_SWITCHES, EventEmitter, SWITCHES, compileOptions, compileScript, compileScripts, compileStdio, exec, fs, helpers, lint, optionParser, optparse, opts, parseOptions, path, printLine, printTokens, printWarn, sources, spawn, usage, utils, version, watch, writeJs, _ref;
+ var ALL_SWITCHES, BANNER, CoffeeScript, DEPRECATED_SWITCHES, EventEmitter, SWITCHES, compileOptions, compileScript, compileScripts, compileStdio, exec, fs, helpers, lint, optionParser, optparse, opts, parseOptions, path, printLine, printTokens, printWarn, sources, spawn, usage, util, version, watch, writeJs, _ref;
fs = require('fs');
path = require('path');
- utils = require('utils');
+ util = require('util');
helpers = require('./helpers');
optparse = require('./optparse');
CoffeeScript = require('./coffee-script');
@@ -186,7 +186,7 @@
if (err) {
return printLine(err.message);
} else if (opts.compile && opts.watch) {
- return utils.log("compiled " + source);
+ return util.log("compiled " + source);
}
});
};
diff --git a/src/command.coffee b/src/command.coffee
index d5aefd37..6034db64 100644
--- a/src/command.coffee
+++ b/src/command.coffee
@@ -7,7 +7,7 @@
# External dependencies.
fs = require 'fs'
path = require 'path'
-utils = require 'utils'
+util = require 'util'
helpers = require './helpers'
optparse = require './optparse'
CoffeeScript = require './coffee-script'
@@ -155,7 +155,7 @@ writeJs = (source, js, base) ->
js = ' ' if js.length <= 0
fs.writeFile jsPath, js, (err) ->
if err then printLine err.message
- else if opts.compile and opts.watch then utils.log "compiled #{source}"
+ else if opts.compile and opts.watch then util.log "compiled #{source}"
path.exists dir, (exists) ->
if exists then compile() else exec "mkdir -p #{dir}", compile
From c0bbc609befc53594f4159ac0c55a0859b299f0c Mon Sep 17 00:00:00 2001
From: Jeremy Ashkenas
Date: Sun, 5 Dec 2010 20:44:32 -0500
Subject: [PATCH 7/8] Fixing direct construction splats.
---
lib/nodes.js | 6 ++++--
src/nodes.coffee | 5 +++--
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/lib/nodes.js b/lib/nodes.js
index 4c092247..db109ddb 100644
--- a/lib/nodes.js
+++ b/lib/nodes.js
@@ -520,8 +520,10 @@
__extends(Call, Base);
Call.prototype.children = ['variable', 'args'];
Call.prototype.newInstance = function() {
- if (this.variable.base instanceof Call) {
- this.variable.base.newInstance();
+ var base;
+ base = this.variable.base || this.variable;
+ if (base instanceof Call) {
+ base.newInstance();
} else {
this.isNew = true;
}
diff --git a/src/nodes.coffee b/src/nodes.coffee
index fc3e176b..8708a0b9 100644
--- a/src/nodes.coffee
+++ b/src/nodes.coffee
@@ -433,8 +433,9 @@ exports.Call = class Call extends Base
# Tag this invocation as creating a new instance.
newInstance: ->
- if @variable.base instanceof Call
- @variable.base.newInstance()
+ base = @variable.base or @variable
+ if base instanceof Call
+ base.newInstance()
else
@isNew = true
this
From 2decb30d4ee1553654ae8801810d6ad7338546f0 Mon Sep 17 00:00:00 2001
From: Jeremy Ashkenas
Date: Sun, 5 Dec 2010 21:18:30 -0500
Subject: [PATCH 8/8] Issue #897 ... fixed leaking direct-call-plucked
comprehension variables, due to shared scope.
---
lib/command.js | 2 +-
lib/nodes.js | 29 +++++++++++++++--------------
lib/scope.js | 3 +++
src/nodes.coffee | 14 +++++++-------
src/scope.coffee | 1 +
test/test_comprehensions.coffee | 15 ++++++++++++++-
6 files changed, 41 insertions(+), 23 deletions(-)
diff --git a/lib/command.js b/lib/command.js
index 7172f651..f3be6632 100644
--- a/lib/command.js
+++ b/lib/command.js
@@ -56,7 +56,7 @@
return compileScripts();
};
compileScripts = function() {
- var base, compile, _fn, _i, _len, _results;
+ var base, compile, source, _fn, _i, _len, _results;
_fn = function(source) {
base = path.join(source);
compile = function(source, topLevel) {
diff --git a/lib/nodes.js b/lib/nodes.js
index db109ddb..5ce2a6da 100644
--- a/lib/nodes.js
+++ b/lib/nodes.js
@@ -1204,9 +1204,12 @@
return !!this.ctor;
};
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');
- 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;
delete o.bare;
delete o.globals;
@@ -1259,7 +1262,7 @@
if (!splats) {
for (i = 0, _len4 = vars.length; i < _len4; i++) {
v = vars[i];
- scope.parameter(vars[i] = v.compile(o));
+ o.scope.parameter(vars[i] = v.compile(o));
}
}
if (!(wasEmpty || this.noReturn)) {
@@ -1769,17 +1772,15 @@
scope = o.scope;
name = this.name && this.name.compile(o, LEVEL_LIST);
index = this.index && this.index.compile(o, LEVEL_LIST);
- if (!hasCode) {
- if (name && !this.pattern) {
- scope.find(name, {
- immediate: true
- });
- }
- if (index) {
- scope.find(index, {
- immediate: true
- });
- }
+ if (name && !this.pattern) {
+ scope.find(name, {
+ immediate: true
+ });
+ }
+ if (index) {
+ scope.find(index, {
+ immediate: true
+ });
}
if (this.returns && !hasPure) {
rvar = scope.freeVariable('results');
diff --git a/lib/scope.js b/lib/scope.js
index 49feb13c..5097955e 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -48,6 +48,9 @@
return false;
};
Scope.prototype.parameter = function(name) {
+ if (this.shared && this.check(name, true)) {
+ return;
+ }
return this.add(name, 'param');
};
Scope.prototype.check = function(name, immediate) {
diff --git a/src/nodes.coffee b/src/nodes.coffee
index 8708a0b9..cfb96ac6 100644
--- a/src/nodes.coffee
+++ b/src/nodes.coffee
@@ -982,9 +982,10 @@ exports.Code = class Code extends Base
# arrow, generates a wrapper that saves the current value of `this` through
# a closure.
compileNode: (o) ->
- sharedScope = del o, 'sharedScope'
- o.scope = scope = sharedScope or new Scope o.scope, @body, this
- o.indent += TAB
+ sharedScope = del o, 'sharedScope'
+ o.scope = sharedScope or new Scope o.scope, @body, this
+ o.scope.shared = yes if sharedScope
+ o.indent += TAB
delete o.bare
delete o.globals
vars = []
@@ -1008,7 +1009,7 @@ exports.Code = class Code extends Base
wasEmpty = @body.isEmpty()
exprs.unshift splats if splats
@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
idt = o.indent
code = 'function'
@@ -1425,9 +1426,8 @@ exports.For = class For extends Base
scope = o.scope
name = @name and @name.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(index, immediate: yes) if index
+ scope.find(name, immediate: yes) if name and not @pattern
+ scope.find(index, immediate: yes) if index
rvar = scope.freeVariable 'results' if @returns and not hasPure
ivar = (if @range then name else index) or scope.freeVariable 'i'
varPart = ''
diff --git a/src/scope.coffee b/src/scope.coffee
index 238d0a9e..cde01160 100644
--- a/src/scope.coffee
+++ b/src/scope.coffee
@@ -44,6 +44,7 @@ exports.Scope = class Scope
# Reserve a variable name as originating from a function parameter for this
# scope. No `var` required for internal references.
parameter: (name) ->
+ return if @shared and @check name, yes
@add name, 'param'
# Just check to see if a variable has already been declared, without reserving,
diff --git a/test/test_comprehensions.coffee b/test/test_comprehensions.coffee
index 86dff3ff..54f802e9 100644
--- a/test/test_comprehensions.coffee
+++ b/test/test_comprehensions.coffee
@@ -227,4 +227,17 @@ foo = ->
for j in [0..7]
-> i + j
-eq foo()[3][4](), 7
\ No newline at end of file
+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'