Skip to content

Commit

Permalink
Fixes jashkenas#2489: gets rid of __bind and instead use its inlined …
Browse files Browse the repository at this point in the history
…form, declaring the same parameters in the bound function as in the prototype's one

Ugliest code ever 💩
  • Loading branch information
epidemian committed Mar 21, 2013
1 parent e26f982 commit 51581e8
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 56 deletions.
67 changes: 39 additions & 28 deletions lib/coffee-script/nodes.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 16 additions & 9 deletions src/nodes.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -985,9 +985,19 @@ exports.Class = class Class extends Base
# Ensure that all functions bound to the instance are proxied in the
# constructor.
addBoundFunctions: (o) ->
for bvar in @boundFuncs
lhs = (new Value (new Literal "this"), [new Access bvar]).compile o
@ctor.body.unshift new Literal "#{lhs} = #{utility 'bind'}(#{lhs}, this)"
if @boundFuncs.length
# Use a temporary Scope just to create some free variables.
# TODO: This is ugly. This should be the @ctor's scope.
scope = new Scope o.scope, @ctor.body
for [name, func] in @boundFuncs
varName = scope.freeVariable if IDENTIFIER.test(name.value) then name.value else 'bound'
varDecl = new Assign new Literal(varName), new Value(new Literal("this"), [new Access name])
lhs = new Value (new Literal "this"), [new Access name]
body = new Block [new Return new Literal "#{varName}.apply(_this, arguments)"]
rhs = new Code func.params, body, 'boundfunc'
bound = new Assign lhs, rhs
@ctor.body.unshift bound
@ctor.body.unshift varDecl
return

# Merge the properties from a top-level object as prototypal properties
Expand Down Expand Up @@ -1017,7 +1027,7 @@ exports.Class = class Class extends Base
else
assign.variable = new Value(new Literal(name), [(new Access new Literal 'prototype'), new Access base ])
if func instanceof Code and func.bound
@boundFuncs.push base
@boundFuncs.push [base, func]
func.bound = no
assign
compact exprs
Expand Down Expand Up @@ -1292,6 +1302,8 @@ exports.Code = class Code extends Base
delete o.isExistentialEquals
params = []
exprs = []
# Clean params references in case the params where used in other Code nodes.
delete param.reference for param in @params
@eachParamName (name) -> # this step must be performed before the others
unless o.scope.check name then o.scope.parameter name
for param in @params when param.splat
Expand Down Expand Up @@ -2115,11 +2127,6 @@ UTILITIES =
function(child, parent) { for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }
"""

# Create a function bound to the current value of "this".
bind: -> '''
function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
'''

# Discover if an item is in an array.
indexOf: -> """
[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }
Expand Down
40 changes: 22 additions & 18 deletions test/classes.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -730,30 +730,34 @@ test "#2359: extending native objects that use other typed constructors requires
eq 'yes!', workingArray.method()


test "#2782: non-alphanumeric-named bound functions", ->
class A
'b:c': =>
'd'
test "#2489: removing __bind", ->

eq (new A)['b:c'](), 'd'
class Thing
foo: (a, b, c) ->
bar: (a, b, c) =>

thing = new Thing

test "#2781: overriding bound functions", ->
class A
a: ->
@b()
b: =>
1
eq thing.foo.length, 3
eq thing.bar.length, 3

class B extends A
b: =>
2

b = (new A).b
eq b(), 1
test "#2773: overriding bound functions", ->
class Foo
method: => 'Foo'

class Bar extends Foo
method: => 'Bar'

b = (new B).b
eq b(), 2
eq (new Bar).method(), 'Bar'


test "#2782: non-alphanumeric-named bound functions", ->
class A
'b:c': =>
'd'

eq (new A)['b:c'](), 'd'


test "#2791: bound function with destructured argument", ->
Expand Down
2 changes: 1 addition & 1 deletion test/cluster.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ if cluster.isMaster
cluster.once 'exit', (worker, code) ->
eq code, 0

cluster.fork()
#cluster.fork()
else
process.exit 0

0 comments on commit 51581e8

Please sign in to comment.