waypoint: part way through refactoring Class

This commit is contained in:
Jeremy Ashkenas
2010-11-13 16:53:55 -05:00
parent cb6793f56b
commit f1972ff336
2 changed files with 109 additions and 98 deletions

View File

@@ -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) {

View File

@@ -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);"