From f1972ff33623b72bfc6f97b31ac0aeea41caabde Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Sat, 13 Nov 2010 16:53:55 -0500 Subject: [PATCH] waypoint: part way through refactoring Class --- lib/nodes.js | 118 +++++++++++++++++++++++++---------------------- src/nodes.coffee | 89 ++++++++++++++++++----------------- 2 files changed, 109 insertions(+), 98 deletions(-) diff --git a/lib/nodes.js b/lib/nodes.js index f6454042..1e5d6b8b 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -7,6 +7,8 @@ for (var key in parent) if (__hasProp.call(parent, key)) child[key] = parent[key]; child.__super__ = parent.prototype; return child; + }, __bind = function(func, context) { + return function() { return func.apply(context, arguments); }; }; Scope = require('./scope').Scope; _ref = require('./helpers'), compact = _ref.compact, flatten = _ref.flatten, extend = _ref.extend, merge = _ref.merge, del = _ref.del, starts = _ref.starts, ends = _ref.ends, last = _ref.last; @@ -820,19 +822,20 @@ this.variable = _arg; this.parent = _arg2; this.body = _arg3 != null ? _arg3 : new Expressions; + this.boundFuncs = []; } __extends(Class, Base); Class.prototype.children = ['variable', 'parent', 'body']; - Class.prototype.compileNode = function(o) { - var bname, boundFuncs, bvar, convert, ctor, decl, exps, i, klass, lname, name, node, others, tail, _fn, _i, _j, _len, _len2, _len3, _ref, _ref2; - ctor = null; - if (this.variable) { - decl = (tail = last(this.variable.properties)) ? tail instanceof Accessor && tail.name.value : this.variable.base.value; - decl && (decl = IDENTIFIER.test(decl) && decl); + Class.prototype.determineName = function() { + var decl, tail; + if (!this.variable) { + return null; } - name = decl || this.name || '_Class'; - lname = new Literal(name); - this.body.traverseChildren(false, function(node) { + decl = (tail = last(this.variable.properties)) ? tail instanceof Accessor && tail.name.value : this.variable.base.value; + return decl && (decl = IDENTIFIER.test(decl) && decl); + }; + Class.prototype.setContext = function(name) { + return this.body.traverseChildren(false, function(node) { if (node instanceof Literal && node.value === 'this') { return node.value = name; } else if (node instanceof Code) { @@ -842,34 +845,39 @@ } } }); - convert = function(node) { - var assign, base, func, props, _results; - props = node.base.properties.slice(0); - _results = []; - while (assign = props.shift()) { - if (assign instanceof Assign) { - base = assign.variable.base; - delete assign.context; - func = assign.value; - if (!assign.variable["this"]) { - assign.variable = new Value(lname, [new Accessor(base, 'proto')]); - } - if (func instanceof Code && func.bound) { - boundFuncs.push(base); - func.bound = false; - } + }; + Class.prototype.addProperties = function(node, name) { + var assign, base, func, props, _results; + props = node.base.properties.slice(0); + _results = []; + while (assign = props.shift()) { + if (assign instanceof Assign) { + base = assign.variable.base; + delete assign.context; + func = assign.value; + if (!assign.variable["this"]) { + assign.variable = new Value(new Literal(name), [new Accessor(base, 'proto')]); + } + if (func instanceof Code && func.bound) { + this.boundFuncs.push(base); + func.bound = false; } - _results.push(assign); } - return _results; - }; - boundFuncs = []; - others = []; + _results.push(assign); + } + return _results; + }; + Class.prototype.compileNode = function(o) { + var bname, bvar, ctor, decl, exps, klass, lname, name, _fn, _i, _len, _len2, _ref, _ref2, _ref3; + ctor = null; + decl = this.determineName(); + name = decl || this.name || '_Class'; + lname = new Literal(name); + this.setContext(name); _ref = exps = this.body.expressions; - for (i = 0, _len = _ref.length; i < _len; i++) { - node = _ref[i]; + _fn = function(node, i) { if (node instanceof Value && node.isObject(true)) { - exps[i] = compact(convert(node)); + return exps[i] = compact(this.addProperties(node, name)); } else if (node instanceof Code) { if (ctor) { throw new Error('cannot define more than one constructor in a class'); @@ -878,29 +886,26 @@ throw new Error('cannot define a constructor as a bound function'); } ctor = node; - exps[i] = null; + return exps[i] = null; } else { - others.push(node); - } - } - _fn = function(other) { - return other.traverseChildren(false, function(n2) { - var expr2, j, _len, _ref; - if (n2 instanceof Expressions) { - _ref = n2.expressions; - for (j = 0, _len = _ref.length; j < _len; j++) { - expr2 = _ref[j]; - if (expr2 instanceof Value && expr2.isObject(true)) { - n2.expressions[j] = compact(convert(expr2)); + return node.traverseChildren(false, __bind(function(n2) { + var expr2, j, _len, _ref; + if (n2 instanceof Expressions) { + _ref = n2.expressions; + for (j = 0, _len = _ref.length; j < _len; j++) { + expr2 = _ref[j]; + if (expr2 instanceof Value && expr2.isObject(true)) { + n2.expressions[j] = compact(this.addProperties(expr2, name)); + } } + return n2.expressions = flatten(n2.expressions); } - return n2.expressions = flatten(n2.expressions); - } - }); + }, this)); + } }; - for (_i = 0, _len2 = others.length; _i < _len2; _i++) { - other = others[_i]; - _fn(other); + for (i = 0, _len = _ref.length; i < _len; i++) { + node = _ref[i]; + _fn.call(this, node, i); } this.body.expressions = exps = compact(flatten(exps)); if (!ctor) { @@ -917,15 +922,16 @@ } exps.unshift(ctor); exps.push(lname); - if (boundFuncs.length) { - for (_j = 0, _len3 = boundFuncs.length; _j < _len3; _j++) { - bvar = boundFuncs[_j]; + if (this.boundFuncs.length) { + _ref2 = this.boundFuncs; + for (_i = 0, _len2 = _ref2.length; _i < _len2; _i++) { + bvar = _ref2[_i]; bname = bvar.compile(o); ctor.body.unshift(new Literal("this." + bname + " = " + (utility('bind')) + "(this." + bname + ", this);")); } } klass = new Parens(new Call(new Code([], this.body)), true); - if (decl && ((_ref2 = this.variable) != null ? _ref2.isComplex() : void 0)) { + if (decl && ((_ref3 = this.variable) != null ? _ref3.isComplex() : void 0)) { klass = new Assign(new Value(lname), klass); } if (this.variable) { diff --git a/src/nodes.coffee b/src/nodes.coffee index 56bebbc5..25c704cd 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -665,22 +665,20 @@ exports.Class = class Class extends Base # Initialize a **Class** with its name, an optional superclass, and a # list of prototype property assignments. (@variable, @parent, @body = new Expressions) -> + @boundFuncs = [] - # Instead of generating the JavaScript string directly, we build up the - # equivalent syntax tree and compile that, in pieces. You can see the - # constructor, property assignments, and inheritance getting built out below. - compileNode: (o) -> - ctor = null + # Figure out the appropriate name for the constructor function of this class. + determineName: -> + return null unless @variable + decl = if tail = last @variable.properties + tail instanceof Accessor and tail.name.value + else + @variable.base.value + decl and= IDENTIFIER.test(decl) and decl - if @variable - decl = if tail = last @variable.properties - tail instanceof Accessor and tail.name.value - else - @variable.base.value - decl and= IDENTIFIER.test(decl) and decl - - name = decl or @name or '_Class' - lname = new Literal name + # For all `this`-references and bound functions in the class definition, + # `this` is the Class being constructed. + setContext: (name) -> @body.traverseChildren false, (node) -> if node instanceof Literal and node.value is 'this' node.value = name @@ -688,25 +686,36 @@ exports.Class = class Class extends Base node.klass = name node.context = name if node.bound - convert = (node) -> - props = node.base.properties.slice 0 - while assign = props.shift() - if assign instanceof Assign - base = assign.variable.base - delete assign.context - func = assign.value - unless assign.variable.this - assign.variable = new Value(lname, [new Accessor(base, 'proto')]) - if func instanceof Code and func.bound - boundFuncs.push base - func.bound = no - assign + # Merge the properties from a top-level object as prototypal properties + # on the class. + addProperties: (node, name) -> + props = node.base.properties.slice 0 + while assign = props.shift() + if assign instanceof Assign + base = assign.variable.base + delete assign.context + func = assign.value + unless assign.variable.this + assign.variable = new Value(new Literal(name), [new Accessor(base, 'proto')]) + if func instanceof Code and func.bound + @boundFuncs.push base + func.bound = no + assign + + # Instead of generating the JavaScript string directly, we build up the + # equivalent syntax tree and compile that, in pieces. You can see the + # constructor, property assignments, and inheritance getting built out below. + compileNode: (o) -> + ctor = null + + decl = @determineName() + name = decl or @name or '_Class' + lname = new Literal name + @setContext name - boundFuncs = [] - others = [] for node, i in exps = @body.expressions if node instanceof Value and node.isObject(true) - exps[i] = compact convert node + exps[i] = compact @addProperties node, name else if node instanceof Code if ctor throw new Error 'cannot define more than one constructor in a class' @@ -715,16 +724,12 @@ exports.Class = class Class extends Base ctor = node exps[i] = null else - others.push node - - # TODO: refactor - for other in others - other.traverseChildren false, (n2) -> - if n2 instanceof Expressions - for expr2, j in n2.expressions - if expr2 instanceof Value and expr2.isObject(true) - n2.expressions[j] = compact convert expr2 - n2.expressions = flatten n2.expressions + node.traverseChildren false, (n2) => + if n2 instanceof Expressions + for expr2, j in n2.expressions + if expr2 instanceof Value and expr2.isObject(true) + n2.expressions[j] = compact @addProperties expr2, name + n2.expressions = flatten n2.expressions @body.expressions = exps = compact flatten exps unless ctor @@ -738,8 +743,8 @@ exports.Class = class Class extends Base exps.unshift ctor exps.push lname - if boundFuncs.length - for bvar in boundFuncs + if @boundFuncs.length + for bvar in @boundFuncs bname = bvar.compile o ctor.body.unshift new Literal "this.#{bname} = #{utility 'bind'}(this.#{bname}, this);"