diff --git a/lib/nodes.js b/lib/nodes.js index 5ce2a6da..f0e96f82 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -162,10 +162,10 @@ return Base; }(); exports.Expressions = Expressions = function() { + __extends(Expressions, Base); function Expressions(nodes) { this.expressions = compact(flatten(nodes || [])); } - __extends(Expressions, Base); Expressions.prototype.children = ['expressions']; Expressions.prototype.push = function(node) { this.expressions.push(node); @@ -298,10 +298,10 @@ return Expressions; }(); exports.Literal = Literal = function() { + __extends(Literal, Base); function Literal(value) { this.value = value; } - __extends(Literal, Base); Literal.prototype.makeReturn = function() { if (this.isPureStatement()) { return this; @@ -333,10 +333,10 @@ return Literal; }(); exports.Return = Return = function() { + __extends(Return, Base); function Return(expression) { this.expression = expression; } - __extends(Return, Base); Return.prototype.children = ['expression']; Return.prototype.isStatement = YES; Return.prototype.isPureStatement = YES; @@ -357,6 +357,7 @@ return Return; }(); exports.Value = Value = function() { + __extends(Value, Base); function Value(base, props, tag) { if (!props && base instanceof Value) { return base; @@ -368,7 +369,6 @@ } return this; } - __extends(Value, Base); Value.prototype.children = ['base', 'properties']; Value.prototype.push = function(prop) { this.properties.push(prop); @@ -492,10 +492,10 @@ return Value; }(); exports.Comment = Comment = function() { + __extends(Comment, Base); function Comment(comment) { this.comment = comment; } - __extends(Comment, Base); Comment.prototype.isPureStatement = YES; Comment.prototype.isStatement = YES; Comment.prototype.makeReturn = THIS; @@ -510,6 +510,7 @@ return Comment; }(); exports.Call = Call = function() { + __extends(Call, Base); function Call(variable, args, soak) { this.args = args != null ? args : []; this.soak = soak; @@ -517,7 +518,6 @@ this.isSuper = variable === 'super'; this.variable = this.isSuper ? null : variable; } - __extends(Call, Base); Call.prototype.children = ['variable', 'args']; Call.prototype.newInstance = function() { var base; @@ -647,11 +647,11 @@ return Call; }(); exports.Extends = Extends = function() { + __extends(Extends, Base); function Extends(child, parent) { this.child = child; this.parent = parent; } - __extends(Extends, Base); Extends.prototype.children = ['child', 'parent']; Extends.prototype.compile = function(o) { utility('hasProp'); @@ -660,12 +660,12 @@ return Extends; }(); exports.Access = Access = function() { + __extends(Access, Base); function Access(name, tag) { this.name = name; this.proto = tag === 'proto' ? '.prototype' : ''; this.soak = tag === 'soak'; } - __extends(Access, Base); Access.prototype.children = ['name']; Access.prototype.compile = function(o) { var name; @@ -676,10 +676,10 @@ return Access; }(); exports.Index = Index = function() { + __extends(Index, Base); function Index(index) { this.index = index; } - __extends(Index, Base); Index.prototype.children = ['index']; Index.prototype.compile = function(o) { return (this.proto ? '.prototype' : '') + ("[" + (this.index.compile(o, LEVEL_PAREN)) + "]"); @@ -690,14 +690,14 @@ return Index; }(); exports.Range = Range = function() { + __extends(Range, Base); + Range.prototype.children = ['from', 'to']; function Range(from, to, tag) { this.from = from; this.to = to; this.exclusive = tag === 'exclusive'; this.equals = this.exclusive ? '' : '='; } - __extends(Range, Base); - Range.prototype.children = ['from', 'to']; Range.prototype.compileVariables = function(o) { var parts, _ref, _ref2, _ref3; o = merge(o, { @@ -775,12 +775,12 @@ return Range; }(); exports.Slice = Slice = function() { + __extends(Slice, Base); + Slice.prototype.children = ['range']; function Slice(range) { this.range = range; Slice.__super__.constructor.call(this); } - __extends(Slice, Base); - Slice.prototype.children = ['range']; Slice.prototype.compileNode = function(o) { var from, to; from = this.range.from ? this.range.from.compile(o) : '0'; @@ -794,11 +794,11 @@ return Slice; }(); exports.Obj = Obj = function() { + __extends(Obj, Base); function Obj(props, generated) { this.generated = generated != null ? generated : false; this.objects = this.properties = props || []; } - __extends(Obj, Base); Obj.prototype.children = ['properties']; Obj.prototype.compileNode = function(o) { var i, idt, indent, join, lastNoncom, nonComments, obj, prop, props, _i, _len, _len2, _ref, _results, _results2; @@ -860,10 +860,10 @@ return Obj; }(); exports.Arr = Arr = function() { + __extends(Arr, Base); function Arr(objs) { this.objects = objs || []; } - __extends(Arr, Base); Arr.prototype.children = ['objects']; Arr.prototype.compileNode = function(o) { var code, obj, _i, _len, _ref, _results; @@ -903,13 +903,13 @@ return Arr; }(); exports.Class = Class = function() { + __extends(Class, Base); function Class(variable, parent, body) { this.variable = variable; this.parent = parent; this.body = body != null ? body : new Expressions; this.boundFuncs = []; } - __extends(Class, Base); Class.prototype.children = ['variable', 'parent', 'body']; Class.prototype.determineName = function() { var decl, tail; @@ -961,11 +961,10 @@ throw new Error('cannot define a constructor as a bound function'); } if (func instanceof Code) { - this.ctor = func; + assign = this.ctor = func; } else { - this.ctor = new Assign(new Value(new Literal(name)), func); + assign = this.ctor = new Assign(new Value(new Literal(name)), func); } - assign = null; } else { if (!assign.variable["this"]) { assign.variable = new Value(new Literal(name), [new Access(base, 'proto')]); @@ -991,10 +990,10 @@ for (i = 0, _len = _ref.length; i < _len; i++) { node = _ref[i]; if (node instanceof Value && node.isObject(true)) { - exps[i] = compact(this.addProperties(node, name)); + exps[i] = this.addProperties(node, name); } } - return child.expressions = exps = compact(flatten(exps)); + return child.expressions = exps = flatten(exps); } }, this)); }; @@ -1004,6 +1003,7 @@ if (this.parent) { this.ctor.body.push(new Call('super', [new Splat(new Literal('arguments'))])); } + this.body.expressions.unshift(this.ctor); } this.ctor.ctor = this.ctor.name = name; this.ctor.klass = null; @@ -1016,11 +1016,10 @@ lname = new Literal(name); this.setContext(name); this.walkBody(name); - this.ensureConstructor(name); if (this.parent) { this.body.expressions.unshift(new Extends(lname, this.parent)); } - this.body.expressions.unshift(this.ctor); + this.ensureConstructor(name); this.body.expressions.push(lname); this.addBoundFunctions(o); klass = new Parens(new Call(new Code([], this.body)), true); @@ -1032,12 +1031,12 @@ return Class; }(); exports.Assign = Assign = function() { + __extends(Assign, Base); function Assign(variable, value, context) { this.variable = variable; this.value = value; this.context = context; } - __extends(Assign, Base); Assign.prototype.METHOD_DEF = /^(?:(\S+)\.prototype\.|\S+?)?\b([$A-Za-z_][$\w]*)$/; Assign.prototype.children = ['variable', 'value']; Assign.prototype.assigns = function(name) { @@ -1190,6 +1189,7 @@ return Assign; }(); exports.Code = Code = function() { + __extends(Code, Base); function Code(params, body, tag) { this.params = params || []; this.body = body || new Expressions; @@ -1198,7 +1198,6 @@ this.context = 'this'; } } - __extends(Code, Base); Code.prototype.children = ['params', 'body']; Code.prototype.isStatement = function() { return !!this.ctor; @@ -1298,12 +1297,12 @@ return Code; }(); exports.Param = Param = function() { + __extends(Param, Base); function Param(name, value, splat) { this.name = name; this.value = value; this.splat = splat; } - __extends(Param, Base); Param.prototype.children = ['name', 'value']; Param.prototype.compile = function(o) { return this.name.compile(o, LEVEL_LIST); @@ -1334,12 +1333,12 @@ return Param; }(); exports.Splat = Splat = function() { - function Splat(name) { - this.name = name.compile ? name : new Literal(name); - } __extends(Splat, Base); Splat.prototype.children = ['name']; Splat.prototype.isAssignable = YES; + function Splat(name) { + this.name = name.compile ? name : new Literal(name); + } Splat.prototype.assigns = function(name) { return this.name.assigns(name); }; @@ -1389,11 +1388,11 @@ return Splat; }(); exports.While = While = function() { + __extends(While, Base); function While(condition, options) { this.condition = (options != null ? options.invert : void 0) ? condition.invert() : condition; this.guard = options != null ? options.guard : void 0; } - __extends(While, Base); While.prototype.children = ['condition', 'guard', 'body']; While.prototype.isStatement = YES; While.prototype.makeReturn = function() { @@ -1452,6 +1451,7 @@ }(); exports.Op = Op = function() { var CONVERSIONS, INVERSIONS; + __extends(Op, Base); function Op(op, first, second, flip) { if (op === 'in') { return new In(first, second); @@ -1470,7 +1470,6 @@ this.flip = !!flip; return this; } - __extends(Op, Base); CONVERSIONS = { '==': '===', '!=': '!==', @@ -1583,11 +1582,11 @@ return Op; }(); exports.In = In = function() { + __extends(In, Base); function In(object, array) { this.object = object; this.array = array; } - __extends(In, Base); In.prototype.children = ['object', 'array']; In.prototype.invert = NEGATE; In.prototype.compileNode = function(o) { @@ -1637,13 +1636,13 @@ return In; }(); exports.Try = Try = function() { + __extends(Try, Base); function Try(attempt, error, recovery, ensure) { this.attempt = attempt; this.error = error; this.recovery = recovery; this.ensure = ensure; } - __extends(Try, Base); Try.prototype.children = ['attempt', 'recovery', 'ensure']; Try.prototype.isStatement = YES; Try.prototype.makeReturn = function() { @@ -1665,10 +1664,10 @@ return Try; }(); exports.Throw = Throw = function() { + __extends(Throw, Base); function Throw(expression) { this.expression = expression; } - __extends(Throw, Base); Throw.prototype.children = ['expression']; Throw.prototype.isStatement = YES; Throw.prototype.makeReturn = THIS; @@ -1678,10 +1677,10 @@ return Throw; }(); exports.Existence = Existence = function() { + __extends(Existence, Base); function Existence(expression) { this.expression = expression; } - __extends(Existence, Base); Existence.prototype.children = ['expression']; Existence.prototype.invert = NEGATE; Existence.prototype.compileNode = function(o) { @@ -1697,10 +1696,10 @@ return Existence; }(); exports.Parens = Parens = function() { + __extends(Parens, Base); function Parens(body) { this.body = body; } - __extends(Parens, Base); Parens.prototype.children = ['body']; Parens.prototype.unwrap = function() { return this.body; @@ -1729,6 +1728,7 @@ return Parens; }(); exports.For = For = function() { + __extends(For, Base); function For(body, source, name, index) { var _ref; this.name = name; @@ -1753,7 +1753,6 @@ } this.returns = false; } - __extends(For, Base); For.prototype.children = ['body', 'source', 'guard', 'step']; For.prototype.isStatement = YES; For.prototype.makeReturn = function() { @@ -1877,12 +1876,12 @@ return For; }(); exports.Switch = Switch = function() { + __extends(Switch, Base); function Switch(subject, cases, otherwise) { this.subject = subject; this.cases = cases; this.otherwise = otherwise; } - __extends(Switch, Base); Switch.prototype.children = ['subject', 'cases', 'otherwise']; Switch.prototype.isStatement = YES; Switch.prototype.makeReturn = function() { @@ -1938,6 +1937,7 @@ return Switch; }(); exports.If = If = function() { + __extends(If, Base); function If(condition, body, options) { this.body = body; if (options == null) { @@ -1948,7 +1948,6 @@ this.isChain = false; this.soak = options.soak; } - __extends(If, Base); If.prototype.children = ['condition', 'body', 'elseBody']; If.prototype.bodyNode = function() { var _ref; diff --git a/lib/scope.js b/lib/scope.js index 5097955e..c9c1cbc5 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -2,6 +2,7 @@ var Scope, extend, last, _ref; _ref = require('./helpers'), extend = _ref.extend, last = _ref.last; exports.Scope = Scope = function() { + Scope.root = null; function Scope(parent, expressions, method) { this.parent = parent; this.expressions = expressions; @@ -17,7 +18,6 @@ Scope.root = this; } } - Scope.root = null; Scope.prototype.add = function(name, type) { var pos; if (typeof (pos = this.positions[name]) === 'number') { diff --git a/src/nodes.coffee b/src/nodes.coffee index cfb96ac6..e871ba76 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -775,10 +775,9 @@ exports.Class = class Class extends Base if func.bound throw new Error 'cannot define a constructor as a bound function' if func instanceof Code - @ctor = func + assign = @ctor = func else - @ctor = new Assign(new Value(new Literal name), func) - assign = null + assign = @ctor = new Assign(new Value(new Literal name), func) else unless assign.variable.this assign.variable = new Value(new Literal(name), [new Access(base, 'proto')]) @@ -794,8 +793,8 @@ exports.Class = class Class extends Base if child instanceof Expressions for node, i in exps = child.expressions if node instanceof Value and node.isObject(true) - exps[i] = compact @addProperties node, name - child.expressions = exps = compact flatten exps + exps[i] = @addProperties node, name + child.expressions = exps = flatten exps # Make sure that a constructor is defined for the class, and properly # configured. @@ -803,6 +802,7 @@ exports.Class = class Class extends Base if not @ctor @ctor = new Code @ctor.body.push new Call 'super', [new Splat new Literal 'arguments'] if @parent + @body.expressions.unshift @ctor @ctor.ctor = @ctor.name = name @ctor.klass = null @ctor.noReturn = yes @@ -817,9 +817,8 @@ exports.Class = class Class extends Base @setContext name @walkBody name - @ensureConstructor name @body.expressions.unshift new Extends lname, @parent if @parent - @body.expressions.unshift @ctor + @ensureConstructor name @body.expressions.push lname @addBoundFunctions o diff --git a/test/test_classes.coffee b/test/test_classes.coffee index 53ce2f9f..b80580c0 100644 --- a/test/test_classes.coffee +++ b/test/test_classes.coffee @@ -331,3 +331,18 @@ class Outer eq (new Outer).label, 'outer' eq (new Outer.Inner).label, 'inner' + + +# Variables in constructor bodies are correctly scoped. +class A + x = 1 + constructor: -> + x = 10 + y = 20 + y = 2 + captured: -> + {x, y} + +a = new A +eq a.captured().x, 10 +eq a.captured().y, 2