Skip to content

Commit 9e3ca4f

Browse files
committed
Fixes jashkenas#2489: gets rid of __bind and instead use its inlined form, declaring the same parameters in the bound function as in the prototype's one
Ugliest code ever 💩
1 parent e26f982 commit 9e3ca4f

File tree

3 files changed

+84
-55
lines changed

3 files changed

+84
-55
lines changed

lib/coffee-script/nodes.js

Lines changed: 44 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/nodes.coffee

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -985,9 +985,21 @@ exports.Class = class Class extends Base
985985
# Ensure that all functions bound to the instance are proxied in the
986986
# constructor.
987987
addBoundFunctions: (o) ->
988-
for bvar in @boundFuncs
989-
lhs = (new Value (new Literal "this"), [new Access bvar]).compile o
990-
@ctor.body.unshift new Literal "#{lhs} = #{utility 'bind'}(#{lhs}, this)"
988+
if @boundFuncs.length
989+
# Use a temporary Scope just to create some free variables.
990+
# TODO: This is ugly. This should be the @ctor's scope.
991+
scope = new Scope o.scope, @ctor.body
992+
exprs = []
993+
for [name, func] in @boundFuncs
994+
varName = scope.freeVariable if IDENTIFIER.test(name.value) then name.value else 'bound'
995+
varDecl = new Assign new Literal(varName), new Value(new Literal("this"), [new Access name])
996+
exprs.push varDecl
997+
lhs = new Value (new Literal "this"), [new Access name]
998+
body = new Block [new Return new Literal "#{varName}.apply(_this, arguments)"]
999+
rhs = new Code func.params, body, 'boundfunc'
1000+
bound = new Assign lhs, rhs
1001+
exprs.push bound
1002+
@ctor.body.unshift expr for expr in exprs by -1
9911003
return
9921004

9931005
# Merge the properties from a top-level object as prototypal properties
@@ -1017,7 +1029,7 @@ exports.Class = class Class extends Base
10171029
else
10181030
assign.variable = new Value(new Literal(name), [(new Access new Literal 'prototype'), new Access base ])
10191031
if func instanceof Code and func.bound
1020-
@boundFuncs.push base
1032+
@boundFuncs.push [base, func]
10211033
func.bound = no
10221034
assign
10231035
compact exprs
@@ -1292,6 +1304,8 @@ exports.Code = class Code extends Base
12921304
delete o.isExistentialEquals
12931305
params = []
12941306
exprs = []
1307+
# Clean params references in case the params where used in other Code nodes.
1308+
delete param.reference for param in @params
12951309
@eachParamName (name) -> # this step must be performed before the others
12961310
unless o.scope.check name then o.scope.parameter name
12971311
for param in @params when param.splat
@@ -2115,11 +2129,6 @@ UTILITIES =
21152129
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; }
21162130
"""
21172131

2118-
# Create a function bound to the current value of "this".
2119-
bind: -> '''
2120-
function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
2121-
'''
2122-
21232132
# Discover if an item is in an array.
21242133
indexOf: -> """
21252134
[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }

test/classes.coffee

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -730,30 +730,34 @@ test "#2359: extending native objects that use other typed constructors requires
730730
eq 'yes!', workingArray.method()
731731

732732

733-
test "#2782: non-alphanumeric-named bound functions", ->
734-
class A
735-
'b:c': =>
736-
'd'
733+
test "#2489: removing __bind", ->
737734

738-
eq (new A)['b:c'](), 'd'
735+
class Thing
736+
foo: (a, b, c) ->
737+
bar: (a, b, c) =>
739738

739+
thing = new Thing
740740

741-
test "#2781: overriding bound functions", ->
742-
class A
743-
a: ->
744-
@b()
745-
b: =>
746-
1
741+
eq thing.foo.length, 3
742+
eq thing.bar.length, 3
747743

748-
class B extends A
749-
b: =>
750-
2
751744

752-
b = (new A).b
753-
eq b(), 1
745+
test "#2773: overriding bound functions", ->
746+
class Foo
747+
method: => 'Foo'
748+
749+
class Bar extends Foo
750+
method: => 'Bar'
754751

755-
b = (new B).b
756-
eq b(), 2
752+
eq (new Bar).method(), 'Bar'
753+
754+
755+
test "#2782: non-alphanumeric-named bound functions", ->
756+
class A
757+
'b:c': =>
758+
'd'
759+
760+
eq (new A)['b:c'](), 'd'
757761

758762

759763
test "#2791: bound function with destructured argument", ->

0 commit comments

Comments
 (0)