Skip to content
Browse files

A trip down the rabbit hole, and a journey back up. So there were thr…

…ee separate bugs, which cascaded:

1. The @ operator didn't work properly within await blocks.  This was an issue with the CpsCascade function, which didn't properly create a `Code` object with the `"boundfunc"` parameter.
2. `autocb` didn't work properly in functions that only had one line --- `await`. The problem is that no `Return` node was being grafted on.  We made an exception for that in the `Block::makeReturn` method.
3. Scoping wasn't right with `__tame_deferrals` -- that needs to be locally scoped, so make that change.
  • Loading branch information...
1 parent 394aea6 commit 1adf30021de98ded34cb6bdba0a7a7856e057904 @maxtaco committed Dec 13, 2011
View
1 lib/coffee-script/browser.js
@@ -71,5 +71,4 @@
} else {
attachEvent('onload', runScripts);
}
-
}).call(this);
View
15 lib/coffee-script/cake.js
@@ -1,6 +1,6 @@
(function() {
- var CoffeeScript, atasks, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tame, tasks;
- var __slice = Array.prototype.slice;
+ var CoffeeScript, atasks, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tame, tasks,
+ __slice = Array.prototype.slice;
tame = {
Deferrals: (function() {
@@ -30,7 +30,6 @@
};
return _Class;
-
})()
};
@@ -74,11 +73,10 @@
return switches.push([letter, flag, description]);
},
invoke: function(name, cb) {
- var t;
- var _this = this;
+ var t,
+ _this = this;
if (!(t = tasks[name])) missingTask(name);
(function(__tame_k) {
- var _this = this;
if (t.async) {
(function(__tame_k) {
var __tame_deferrals;
@@ -118,8 +116,8 @@
_len = _ref.length;
_i = 0;
_while = function(__tame_k) {
- var _break, _continue;
- var _this = this;
+ var _break, _continue,
+ _this = this;
_break = __tame_k;
_continue = function() {
++_i;
@@ -173,5 +171,4 @@
if (parent !== dir) return cakefileDirectory(parent);
throw new Error("Cakefile not found in " + (process.cwd()));
};
-
}).call(this);
View
5 lib/coffee-script/coffee-script.js
@@ -1,6 +1,6 @@
(function() {
- var Lexer, RESERVED, compile, fs, lexer, parser, path, tame, vm, _ref;
- var __hasProp = Object.prototype.hasOwnProperty;
+ var Lexer, RESERVED, compile, fs, lexer, parser, path, tame, vm, _ref,
+ __hasProp = Object.prototype.hasOwnProperty;
fs = require('fs');
@@ -150,5 +150,4 @@
parser.yy = require('./nodes');
exports.tame = tame.runtime;
-
}).call(this);
View
1 lib/coffee-script/command.js
@@ -431,5 +431,4 @@
version = function() {
return printLine("CoffeeScript version " + CoffeeScript.VERSION);
};
-
}).call(this);
View
1 lib/coffee-script/grammar.js
@@ -608,5 +608,4 @@
operators: operators.reverse(),
startSymbol: 'Root'
});
-
}).call(this);
View
1 lib/coffee-script/helpers.js
@@ -69,5 +69,4 @@
exports.last = function(array, back) {
return array[array.length - (back || 0) - 1];
};
-
}).call(this);
View
1 lib/coffee-script/index.js
@@ -7,5 +7,4 @@
val = _ref[key];
exports[key] = val;
}
-
}).call(this);
View
7 lib/coffee-script/lexer.js
@@ -1,6 +1,7 @@
(function() {
- var BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref, _ref2;
- var __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; };
+ var BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref, _ref2,
+ __hasProp = Object.prototype.hasOwnProperty,
+ __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; };
_ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES;
@@ -610,7 +611,6 @@
};
return Lexer;
-
})();
JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally', 'class', 'extends', 'super'];
@@ -707,5 +707,4 @@
INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL');
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
-
}).call(this);
View
64 lib/coffee-script/nodes.js
@@ -1,6 +1,8 @@
(function() {
- var Access, Arr, Assign, Await, Base, Block, CALL_CONTINUATION, Call, Class, Closure, Code, Comment, CpsCascade, Defer, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, InlineDeferral, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, Scope, Slice, Slot, Splat, Switch, TAB, THIS, TameRequire, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, starts, tame, unfoldSoak, utility, _ref;
- var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; };
+ var Access, Arr, Assign, Await, Base, Block, CALL_CONTINUATION, Call, Class, Closure, Code, Comment, CpsCascade, Defer, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, InlineDeferral, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, Scope, Slice, Slot, Splat, Switch, TAB, THIS, TameRequire, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, starts, tame, unfoldSoak, utility, _ref,
+ __hasProp = Object.prototype.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; },
+ __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; };
Scope = require('./scope').Scope;
@@ -349,7 +351,6 @@
Base.prototype.assigns = NO;
return Base;
-
})();
exports.Block = Block = (function() {
@@ -414,7 +415,7 @@
expr = this.expressions[len];
if (!(expr instanceof Comment)) {
this.expressions[len] = expr.makeReturn(res);
- if (expr instanceof Return && !expr.expression) {
+ if (expr instanceof Return && !expr.expression && !expr.hasAutocb) {
this.expressions.splice(len, 1);
}
break;
@@ -593,6 +594,11 @@
return new Block(nodes);
};
+ Block.prototype.isOnlyAwait = function() {
+ var _ref2;
+ return ((_ref2 = this.expressions) != null ? _ref2.length : void 0) === 1 && this.expressions[0] instanceof Await;
+ };
+
Block.prototype.addRuntime = function() {
return this.expressions.unshift(new TameRequire());
};
@@ -608,7 +614,6 @@
};
return Block;
-
})();
exports.Literal = Literal = (function() {
@@ -680,7 +685,6 @@
};
return Literal;
-
})();
exports.Return = Return = (function() {
@@ -725,7 +729,6 @@
};
return Return;
-
})();
exports.Value = Value = (function() {
@@ -855,8 +858,8 @@
};
Value.prototype.unfoldSoak = function(o) {
- var result;
- var _this = this;
+ var result,
+ _this = this;
if (this.unfoldedSoak != null) return this.unfoldedSoak;
result = (function() {
var fst, i, ifn, prop, ref, snd, _len, _ref2;
@@ -886,7 +889,6 @@
};
return Value;
-
})();
exports.Comment = Comment = (function() {
@@ -909,7 +911,6 @@
};
return Comment;
-
})();
exports.Call = Call = (function() {
@@ -1078,7 +1079,6 @@
};
return Call;
-
})();
exports.Extends = Extends = (function() {
@@ -1097,7 +1097,6 @@
};
return Extends;
-
})();
exports.Access = Access = (function() {
@@ -1125,7 +1124,6 @@
Access.prototype.isComplex = NO;
return Access;
-
})();
exports.Index = Index = (function() {
@@ -1147,7 +1145,6 @@
};
return Index;
-
})();
exports.Range = Range = (function() {
@@ -1226,7 +1223,6 @@
};
return Range;
-
})();
exports.Slice = Slice = (function() {
@@ -1252,7 +1248,6 @@
};
return Slice;
-
})();
exports.Obj = Obj = (function() {
@@ -1318,7 +1313,6 @@
};
return Obj;
-
})();
exports.Arr = Arr = (function() {
@@ -1366,7 +1360,6 @@
};
return Arr;
-
})();
exports.Class = Class = (function() {
@@ -1515,7 +1508,6 @@
};
return Class;
-
})();
exports.Assign = Assign = (function() {
@@ -1713,7 +1705,6 @@
};
return Assign;
-
})();
exports.Code = Code = (function() {
@@ -1746,6 +1737,7 @@
break;
}
}
+ if (found && this.body.isOnlyAwait()) this.body.push(new Return(null, true));
return Code.__super__.markAutocbs.call(this, found);
};
@@ -1857,7 +1849,6 @@
};
return Code;
-
})();
exports.Param = Param = (function() {
@@ -1896,7 +1887,6 @@
};
return Param;
-
})();
exports.Splat = Splat = (function() {
@@ -1966,7 +1956,6 @@
};
return Splat;
-
})();
exports.While = While = (function() {
@@ -2084,7 +2073,6 @@
};
return While;
-
})();
exports.Op = Op = (function() {
@@ -2236,7 +2224,6 @@
};
return Op;
-
})();
exports.In = In = (function() {
@@ -2308,7 +2295,6 @@
};
return In;
-
})();
exports.Slot = Slot = (function() {
@@ -2324,7 +2310,6 @@
Slot.prototype.children = ['value', 'suffix'];
return Slot;
-
})();
exports.Defer = Defer = (function() {
@@ -2433,7 +2418,6 @@
};
return Defer;
-
})();
exports.Await = Await = (function() {
@@ -2444,10 +2428,12 @@
this.body = body;
}
- Await.prototype.transform = function() {
- var assign, body, call, cls, k, lhs, meth, rhs;
+ Await.prototype.transform = function(o) {
+ var assign, body, call, cls, k, lhs, meth, name, rhs;
body = this.body;
- lhs = new Value(new Literal(tame["const"].deferrals));
+ name = tame["const"].deferrals;
+ o.scope.add(name, 'var');
+ lhs = new Value(new Literal(name));
cls = new Value(new Literal(tame["const"].ns));
cls.add(new Access(new Value(new Literal(tame["const"].Deferrals))));
call = new Call(cls, [new Value(new Literal(tame["const"].k))]);
@@ -2470,7 +2456,7 @@
Await.prototype.makeReturn = THIS;
Await.prototype.compileNode = function(o) {
- this.transform();
+ this.transform(o);
return this.body.compile(o);
};
@@ -2480,7 +2466,6 @@
};
return Await;
-
})();
exports.TameRequire = TameRequire = (function() {
@@ -2527,7 +2512,6 @@
};
return TameRequire;
-
})();
exports.Try = Try = (function() {
@@ -2567,7 +2551,6 @@
};
return Try;
-
})();
exports.Throw = Throw = (function() {
@@ -2591,7 +2574,6 @@
};
return Throw;
-
})();
exports.Existence = Existence = (function() {
@@ -2624,7 +2606,6 @@
};
return Existence;
-
})();
exports.Parens = Parens = (function() {
@@ -2662,7 +2643,6 @@
};
return Parens;
-
})();
exports.For = For = (function() {
@@ -2876,7 +2856,6 @@
};
return For;
-
})();
exports.Switch = Switch = (function() {
@@ -2962,7 +2941,6 @@
};
return Switch;
-
})();
exports.If = If = (function() {
@@ -3088,7 +3066,6 @@
};
return If;
-
})();
Closure = {
@@ -3122,7 +3099,7 @@
CpsCascade = {
wrap: function(statement, rest) {
var block, call, cont, func;
- func = new Code([new Param(new Literal(tame["const"].k))], Block.wrap([statement]));
+ func = new Code([new Param(new Literal(tame["const"].k))], Block.wrap([statement]), 'boundfunc');
block = Block.wrap([rest]);
cont = new Code([], block, 'boundfunc');
call = new Call(func, [cont]);
@@ -3250,5 +3227,4 @@
code = code.replace(/\n/g, '$&' + tab);
return code.replace(/\s+$/, '');
};
-
}).call(this);
View
2 lib/coffee-script/optparse.js
@@ -71,7 +71,6 @@
};
return OptionParser;
-
})();
LONG_FLAG = /^(--\w[\w\-]+)/;
@@ -126,5 +125,4 @@
}
return result;
};
-
}).call(this);
View
1 lib/coffee-script/repl.js
@@ -213,5 +213,4 @@
repl.setPrompt(REPL_PROMPT);
repl.prompt();
-
}).call(this);
View
8 lib/coffee-script/rewriter.js
@@ -1,6 +1,8 @@
(function() {
- var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref;
- var __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; }, __slice = Array.prototype.slice;
+ var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref,
+ __hasProp = Object.prototype.hasOwnProperty,
+ __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; },
+ __slice = Array.prototype.slice;
exports.Rewriter = (function() {
@@ -260,7 +262,6 @@
};
return Rewriter;
-
})();
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['CALL_START', 'CALL_END'], ['PARAM_START', 'PARAM_END'], ['INDEX_START', 'INDEX_END']];
@@ -294,5 +295,4 @@
SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN'];
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT'];
-
}).call(this);
View
2 lib/coffee-script/scope.js
@@ -121,7 +121,5 @@
};
return Scope;
-
})();
-
}).call(this);
View
8 lib/coffee-script/tame.js
@@ -1,6 +1,6 @@
(function() {
- var Deferrals, Rendezvous, makeDeferReturn;
- var __slice = Array.prototype.slice;
+ var Deferrals, Rendezvous, makeDeferReturn,
+ __slice = Array.prototype.slice;
exports.transform = function(x) {
return x.tameTransform();
@@ -62,7 +62,6 @@
};
return Deferrals;
-
})();
Rendezvous = (function() {
@@ -87,7 +86,6 @@
};
return RvId;
-
})();
Rendezvous.prototype.wait = function(cb) {
@@ -129,12 +127,10 @@
};
return Rendezvous;
-
})();
exports.runtime = {
Deferrals: Deferrals,
Rendezvous: Rendezvous
};
-
}).call(this);
View
19 src/nodes.coffee
@@ -332,7 +332,7 @@ exports.Block = class Block extends Base
expr = @expressions[len]
if expr not instanceof Comment
@expressions[len] = expr.makeReturn res
- @expressions.splice(len, 1) if expr instanceof Return and not expr.expression
+ @expressions.splice(len, 1) if expr instanceof Return and not expr.expression and not expr.hasAutocb
break
this
@@ -500,6 +500,9 @@ exports.Block = class Block extends Base
return nodes[0] if nodes.length is 1 and nodes[0] instanceof Block
new Block nodes
+ isOnlyAwait : ->
+ return @expressions?.length == 1 and @expressions[0] instanceof Await
+
addRuntime : ->
@expressions.unshift new TameRequire()
@@ -1410,6 +1413,10 @@ exports.Code = class Code extends Base
if p.name instanceof Literal and p.name.value == tame.const.autocb
found = true
break
+ # for functions that call await and do nothing else, and have an
+ # autocb, we need to put in a dummy return so the autocb is triggered
+ if found and @body.isOnlyAwait()
+ @body.push new Return(null, true)
super(found)
# Compilation creates a new scope unless explicitly asked to share with the
@@ -1996,9 +2003,11 @@ exports.Await = class Await extends Base
constructor : (body) ->
@body = body
- transform : ->
+ transform : (o) ->
body = @body
- lhs = new Value new Literal tame.const.deferrals
+ name = tame.const.deferrals
+ o.scope.add name, 'var'
+ lhs = new Value new Literal name
cls = new Value new Literal tame.const.ns
cls.add(new Access(new Value new Literal tame.const.Deferrals))
call = new Call cls, [ new Value new Literal tame.const.k ]
@@ -2019,7 +2028,7 @@ exports.Await = class Await extends Base
makeReturn : THIS
compileNode: (o) ->
- @transform()
+ @transform(o)
@body.compile o
# We still need to walk our children to see if there are any embedded
@@ -2553,7 +2562,7 @@ Closure =
CpsCascade =
wrap: (statement, rest) ->
- func = new Code [ new Param new Literal tame.const.k ], Block.wrap [ statement ]
+ func = new Code [ new Param new Literal tame.const.k ], (Block.wrap [ statement ]), 'boundfunc'
block = Block.wrap [ rest ]
cont = new Code [], block, 'boundfunc'
call = new Call func, [ cont ]
View
32 test/tame.coffee
@@ -136,16 +136,30 @@ atest "test nested serial/parallel", (cb) ->
slots = []
await
for i in [0..10]
- ( (autocb) ->
+ ( (j, autocb) ->
await delay defer(), 5*Math.random()
await delay defer(), 4*Math.random()
- slots[i] = true
- )(defer())
+ slots[j] = true
+ )(i, defer())
ok = true
- for slot in slots
- ok = false unless slot
+ for i in [0..10]
+ ok = false unless slots[i]
cb(ok, {})
+atest "AT variable works in an await", (cb) ->
+ class MyClass
+ constructor : ->
+ @flag = false
+ chill : (autocb) ->
+ await delay defer()
+ run : (autocb) ->
+ await @chill defer()
+ @flag = true
+ getFlag : -> @flag
+ o = new MyClass
+ await o.run defer()
+ cb(true, {})
+
atest "more advanced autocb test", (cb) ->
bar = -> "yoyo"
foo = (val, autocb) ->
@@ -164,3 +178,11 @@ atest "more advanced autocb test", (cb) ->
await foo 100, defer x
oks++ if x == 33
cb(oks == 4, {})
+
+atest "test of autocb in a simple function", (cb) ->
+ simple = (autocb) ->
+ await delay defer()
+ ok = false
+ await simple defer()
+ ok = true
+ cb(ok,{})

0 comments on commit 1adf300

Please sign in to comment.
Something went wrong with that request. Please try again.