From 0bad04d374a141f638b690c689b33416d49b9b0a Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 27 Apr 2011 21:56:08 -0400 Subject: [PATCH] Issue #1182 -- subclasses with external constructor functions. --- lib/nodes.js | 62 ++++++++++++++++++++++----------------------- src/nodes.coffee | 2 +- test/classes.coffee | 11 ++++++++ 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/lib/nodes.js b/lib/nodes.js index 3f917bfb..55324041 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -167,10 +167,10 @@ return Base; })(); exports.Block = Block = (function() { - __extends(Block, Base); function Block(nodes) { this.expressions = compact(flatten(nodes || [])); } + __extends(Block, Base); Block.prototype.children = ['expressions']; Block.prototype.push = function(node) { this.expressions.push(node); @@ -319,10 +319,10 @@ return Block; })(); exports.Literal = Literal = (function() { - __extends(Literal, Base); function Literal(value) { this.value = value; } + __extends(Literal, Base); Literal.prototype.makeReturn = function() { if (this.isStatement()) { return this; @@ -366,12 +366,12 @@ return Literal; })(); exports.Return = Return = (function() { - __extends(Return, Base); function Return(expr) { if (expr && !expr.unwrap().isUndefined) { this.expression = expr; } } + __extends(Return, Base); Return.prototype.children = ['expression']; Return.prototype.isStatement = YES; Return.prototype.makeReturn = THIS; @@ -391,7 +391,6 @@ return Return; })(); exports.Value = Value = (function() { - __extends(Value, Base); function Value(base, props, tag) { if (!props && base instanceof Value) { return base; @@ -403,6 +402,7 @@ } return this; } + __extends(Value, Base); Value.prototype.children = ['base', 'properties']; Value.prototype.push = function(prop) { this.properties.push(prop); @@ -529,10 +529,10 @@ return Value; })(); exports.Comment = Comment = (function() { - __extends(Comment, Base); function Comment(comment) { this.comment = comment; } + __extends(Comment, Base); Comment.prototype.isStatement = YES; Comment.prototype.makeReturn = THIS; Comment.prototype.compileNode = function(o, level) { @@ -546,7 +546,6 @@ return Comment; })(); exports.Call = Call = (function() { - __extends(Call, Base); function Call(variable, args, soak) { this.args = args != null ? args : []; this.soak = soak; @@ -554,6 +553,7 @@ this.isSuper = variable === 'super'; this.variable = this.isSuper ? null : variable; } + __extends(Call, Base); Call.prototype.children = ['variable', 'args']; Call.prototype.newInstance = function() { var base; @@ -713,11 +713,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'); @@ -726,13 +726,13 @@ return Extends; })(); exports.Access = Access = (function() { - __extends(Access, Base); function Access(name, tag) { this.name = name; this.name.asKey = true; this.proto = tag === 'proto' ? '.prototype' : ''; this.soak = tag === 'soak'; } + __extends(Access, Base); Access.prototype.children = ['name']; Access.prototype.compile = function(o) { var name; @@ -743,10 +743,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)) + "]"); @@ -757,8 +757,8 @@ return Index; })(); exports.Range = Range = (function() { - __extends(Range, Base); Range.prototype.children = ['from', 'to']; + __extends(Range, Base); function Range(from, to, tag) { this.from = from; this.to = to; @@ -841,8 +841,8 @@ return Range; })(); exports.Slice = Slice = (function() { - __extends(Slice, Base); Slice.prototype.children = ['range']; + __extends(Slice, Base); function Slice(range) { this.range = range; Slice.__super__.constructor.call(this); @@ -860,11 +860,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, node, obj, prop, props, _i, _len; @@ -928,10 +928,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.filterImplicitObjects = Call.prototype.filterImplicitObjects; Arr.prototype.compileNode = function(o) { @@ -973,7 +973,6 @@ return Arr; })(); exports.Class = Class = (function() { - __extends(Class, Base); function Class(variable, parent, body) { this.variable = variable; this.parent = parent; @@ -981,6 +980,7 @@ this.boundFuncs = []; this.body.classBody = true; } + __extends(Class, Base); Class.prototype.children = ['variable', 'parent', 'body']; Class.prototype.determineName = function() { var decl, tail; @@ -1090,10 +1090,10 @@ lname = new Literal(name); this.setContext(name); this.walkBody(name); - if (this.parent) { - this.body.expressions.unshift(new Extends(lname, this.parent)); - } this.ensureConstructor(name); + if (this.parent) { + this.body.expressions.splice(1, 0, new Extends(lname, this.parent)); + } this.body.expressions.push(lname); this.addBoundFunctions(o); klass = new Parens(Closure.wrap(this.body), true); @@ -1105,13 +1105,13 @@ return Class; })(); exports.Assign = Assign = (function() { - __extends(Assign, Base); function Assign(variable, value, context, options) { this.variable = variable; this.value = value; this.context = context; this.param = options && options.param; } + __extends(Assign, Base); Assign.prototype.METHOD_DEF = /^(?:(\S+)\.prototype\.|\S+?)?\b([$A-Za-z_][$\w\x7f-\uffff]*)$/; Assign.prototype.children = ['variable', 'value']; Assign.prototype.assigns = function(name) { @@ -1284,7 +1284,6 @@ return Assign; })(); exports.Code = Code = (function() { - __extends(Code, Base); function Code(params, body, tag) { this.params = params || []; this.body = body || new Block; @@ -1293,6 +1292,7 @@ this.context = 'this'; } } + __extends(Code, Base); Code.prototype.children = ['params', 'body']; Code.prototype.isStatement = function() { return !!this.ctor; @@ -1396,12 +1396,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); @@ -1432,8 +1432,8 @@ return Param; })(); exports.Splat = Splat = (function() { - __extends(Splat, Base); Splat.prototype.children = ['name']; + __extends(Splat, Base); Splat.prototype.isAssignable = YES; function Splat(name) { this.name = name.compile ? name : new Literal(name); @@ -1488,11 +1488,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() { @@ -1549,7 +1549,6 @@ })(); exports.Op = Op = (function() { var CONVERSIONS, INVERSIONS; - __extends(Op, Base); function Op(op, first, second, flip) { var call; if (op === 'in') { @@ -1574,6 +1573,7 @@ this.flip = !!flip; return this; } + __extends(Op, Base); CONVERSIONS = { '==': '===', '!=': '!==', @@ -1689,11 +1689,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) { @@ -1747,13 +1747,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.jumps = function(o) { @@ -1779,10 +1779,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.jumps = NO; @@ -1793,10 +1793,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) { @@ -1812,10 +1812,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; @@ -1844,7 +1844,6 @@ return Parens; })(); exports.For = For = (function() { - __extends(For, Base); function For(body, source) { var _ref2; this.source = source.source, this.guard = source.guard, this.step = source.step, this.name = source.name, this.index = source.index; @@ -1867,6 +1866,7 @@ } this.returns = false; } + __extends(For, Base); For.prototype.children = ['body', 'source', 'guard', 'step']; For.prototype.isStatement = YES; For.prototype.jumps = While.prototype.jumps; @@ -1984,12 +1984,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.jumps = function(o) { @@ -2056,7 +2056,6 @@ return Switch; })(); exports.If = If = (function() { - __extends(If, Base); function If(condition, body, options) { this.body = body; if (options == null) { @@ -2067,6 +2066,7 @@ this.isChain = false; this.soak = options.soak; } + __extends(If, Base); If.prototype.children = ['condition', 'body', 'elseBody']; If.prototype.bodyNode = function() { var _ref2; diff --git a/src/nodes.coffee b/src/nodes.coffee index 34cece36..f8cbf731 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -869,8 +869,8 @@ exports.Class = class Class extends Base @setContext name @walkBody name - @body.expressions.unshift new Extends lname, @parent if @parent @ensureConstructor name + @body.expressions.splice 1, 0, new Extends(lname, @parent) if @parent @body.expressions.push lname @addBoundFunctions o diff --git a/test/classes.coffee b/test/classes.coffee index 17d5f581..6b890c68 100644 --- a/test/classes.coffee +++ b/test/classes.coffee @@ -436,3 +436,14 @@ test "`new` works against bare function", -> eq Date, new -> eq this, new => this Date + + +test "a subclass should be able to set its constructor to an external function", -> + + ctor = -> + @val = 1 + class A + class B extends A + constructor: ctor + + eq (new B).val, 1 \ No newline at end of file