more streamlined code generation for instance-bound methods ... keep the prototype method around, and just bind it in the constructor.

This commit is contained in:
Jeremy Ashkenas
2010-06-15 02:21:01 -04:00
parent 8d853a6d58
commit 06ca2ef726
2 changed files with 42 additions and 24 deletions

View File

@@ -895,10 +895,13 @@
// equivalent syntax tree and compile that, in pieces. You can see the // equivalent syntax tree and compile that, in pieces. You can see the
// constructor, property assignments, and inheritance getting built out below. // constructor, property assignments, and inheritance getting built out below.
ClassNode.prototype.compileNode = function(o) { ClassNode.prototype.compileNode = function(o) {
var _b, _c, _d, _e, access, applied, construct, extension, func, prop, props, pvar, returns, val; var _b, _c, _d, _e, access, applied, className, constScope, construct, extension, func, me, pname, prop, props, pvar, returns, val;
extension = this.parent && new ExtendsNode(this.variable, this.parent); extension = this.parent && new ExtendsNode(this.variable, this.parent);
props = new Expressions(); props = new Expressions();
o.top = true; o.top = true;
me = null;
className = this.variable.compile(o);
constScope = null;
if (this.parent) { if (this.parent) {
applied = new ValueNode(this.parent, [new AccessorNode(literal('apply'))]); applied = new ValueNode(this.parent, [new AccessorNode(literal('apply'))]);
constructor = new CodeNode([], new Expressions([new CallNode(applied, [literal('this'), literal('arguments')])])); constructor = new CodeNode([], new Expressions([new CallNode(applied, [literal('this'), literal('arguments')])]));
@@ -912,7 +915,7 @@
pvar = _e[0]; pvar = _e[0];
func = _e[1]; func = _e[1];
if (pvar && pvar.base.value === 'constructor' && func instanceof CodeNode) { if (pvar && pvar.base.value === 'constructor' && func instanceof CodeNode) {
func.name = this.variable.compile(o); func.name = className;
func.body.push(new ReturnNode(literal('this'))); func.body.push(new ReturnNode(literal('this')));
this.variable = new ValueNode(this.variable); this.variable = new ValueNode(this.variable);
this.variable.namespaced = include(func.name, '.'); this.variable.namespaced = include(func.name, '.');
@@ -920,20 +923,28 @@
continue; continue;
} }
if (func instanceof CodeNode && func.bound) { if (func instanceof CodeNode && func.bound) {
func.bound = false;
constScope = constScope || new Scope(o.scope, constructor.body, constructor);
me = me || constScope.freeVariable();
pname = pvar.compile(o);
if (constructor.body.empty()) { if (constructor.body.empty()) {
constructor.body.push(new ReturnNode(literal('this'))); constructor.body.push(new ReturnNode(literal('this')));
} }
constructor.body.unshift(new AssignNode(new ValueNode(literal('this'), [new AccessorNode(pvar)]), func)); constructor.body.unshift(literal(("this." + (pname) + " = function(){ return " + (className) + ".prototype." + (pname) + ".apply(" + me + ", arguments); }")));
} else {
if (pvar) {
access = prop.context === 'this' ? pvar.base.properties[0] : new AccessorNode(pvar, 'prototype');
val = new ValueNode(this.variable, [access]);
prop = new AssignNode(val, func);
}
props.push(prop);
} }
if (pvar) {
access = prop.context === 'this' ? pvar.base.properties[0] : new AccessorNode(pvar, 'prototype');
val = new ValueNode(this.variable, [access]);
prop = new AssignNode(val, func);
}
props.push(prop);
} }
construct = this.idt() + (new AssignNode(this.variable, constructor)).compile(o) + ';\n'; if (me) {
constructor.body.unshift(literal(("" + me + " = this")));
}
construct = this.idt() + (new AssignNode(this.variable, constructor)).compile(merge(o, {
sharedScope: constScope
})) + ';\n';
props = props.empty() ? '' : props.compile(o) + '\n'; props = props.empty() ? '' : props.compile(o) + '\n';
extension = extension ? this.idt() + extension.compile(o) + ';\n' : ''; extension = extension ? this.idt() + extension.compile(o) + ';\n' : '';
returns = this.returns ? new ReturnNode(this.variable).compile(o) : ''; returns = this.returns ? new ReturnNode(this.variable).compile(o) : '';

View File

@@ -655,9 +655,12 @@ exports.ClassNode: class ClassNode extends BaseNode
# equivalent syntax tree and compile that, in pieces. You can see the # equivalent syntax tree and compile that, in pieces. You can see the
# constructor, property assignments, and inheritance getting built out below. # constructor, property assignments, and inheritance getting built out below.
compileNode: (o) -> compileNode: (o) ->
extension: @parent and new ExtendsNode(@variable, @parent) extension: @parent and new ExtendsNode(@variable, @parent)
props: new Expressions() props: new Expressions()
o.top: true o.top: true
me: null
className: @variable.compile o
constScope: null
if @parent if @parent
applied: new ValueNode(@parent, [new AccessorNode(literal('apply'))]) applied: new ValueNode(@parent, [new AccessorNode(literal('apply'))])
@@ -670,23 +673,27 @@ exports.ClassNode: class ClassNode extends BaseNode
for prop in @properties for prop in @properties
[pvar, func]: [prop.variable, prop.value] [pvar, func]: [prop.variable, prop.value]
if pvar and pvar.base.value is 'constructor' and func instanceof CodeNode if pvar and pvar.base.value is 'constructor' and func instanceof CodeNode
func.name: @variable.compile(o) func.name: className
func.body.push new ReturnNode literal 'this' func.body.push new ReturnNode literal 'this'
@variable: new ValueNode @variable @variable: new ValueNode @variable
@variable.namespaced: include func.name, '.' @variable.namespaced: include func.name, '.'
constructor: func constructor: func
continue continue
if func instanceof CodeNode and func.bound if func instanceof CodeNode and func.bound
func.bound: false
constScope: or new Scope(o.scope, constructor.body, constructor)
me: or constScope.freeVariable()
pname: pvar.compile(o)
constructor.body.push new ReturnNode literal 'this' if constructor.body.empty() constructor.body.push new ReturnNode literal 'this' if constructor.body.empty()
constructor.body.unshift new AssignNode(new ValueNode(literal('this'), [new AccessorNode(pvar)]), func) constructor.body.unshift literal "this.${pname} = function(){ return ${className}.prototype.${pname}.apply($me, arguments); }"
else if pvar
if pvar access: if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype')
access: if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype') val: new ValueNode(@variable, [access])
val: new ValueNode(@variable, [access]) prop: new AssignNode(val, func)
prop: new AssignNode(val, func) props.push prop
props.push prop
construct: @idt() + (new AssignNode(@variable, constructor)).compile(o) + ';\n' constructor.body.unshift literal "$me = this" if me
construct: @idt() + (new AssignNode(@variable, constructor)).compile(merge o, {sharedScope: constScope}) + ';\n'
props: if props.empty() then '' else props.compile(o) + '\n' props: if props.empty() then '' else props.compile(o) + '\n'
extension: if extension then @idt() + extension.compile(o) + ';\n' else '' extension: if extension then @idt() + extension.compile(o) + ';\n' else ''
returns: if @returns then new ReturnNode(@variable).compile(o) else '' returns: if @returns then new ReturnNode(@variable).compile(o) else ''
@@ -815,7 +822,7 @@ exports.CodeNode: class CodeNode extends BaseNode
# arrow, generates a wrapper that saves the current value of `this` through # arrow, generates a wrapper that saves the current value of `this` through
# a closure. # a closure.
compileNode: (o) -> compileNode: (o) ->
sharedScope: del o, 'sharedScope' sharedScope: del o, 'sharedScope'
top: del o, 'top' top: del o, 'top'
o.scope: sharedScope or new Scope(o.scope, @body, this) o.scope: sharedScope or new Scope(o.scope, @body, this)
o.top: true o.top: true