Rewrite Function.bind as a builtin
[WebKit.git] / Source / JavaScriptCore / builtins / Function.prototype.js
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 function call(thisArgument) {
27     "use strict";
28     return this.@call(...arguments);
29 }
30
31 function apply(thisValue, argumentValues) {
32     "use strict";
33     return this.@apply(thisValue, argumentValues);
34 }
35
36 function bind(thisValue /*, optional arguments */)
37 {
38     "use strict";
39     var boundFunction = this;
40     if (typeof boundFunction !== "function")
41         throw new @TypeError("Cannot bind non-function object.");
42     var bindingFunction;
43     var oversizedCall = undefined;
44     if (arguments.length <= 1) {
45         bindingFunction = function () {
46             if (@IsConstructor)
47                 return new boundFunction(...arguments);
48             return boundFunction.@apply(thisValue, arguments);
49         }
50     } else {
51         var length = arguments.length;
52         switch (length - 1 /* skip thisValue */) {
53         case 1: {
54             var boundParameter = arguments[1];
55             bindingFunction = function () {
56                 var argumentLength = arguments.length;
57                 if (!argumentLength) {
58                     if (@IsConstructor)
59                         return new boundFunction(boundParameter);
60                     return boundFunction.@call(thisValue, boundParameter);
61                 }
62                 if (argumentLength == 1) {
63                     if (@IsConstructor)
64                         return new boundFunction(boundParameter, arguments[0]);
65                     return boundFunction.@call(thisValue, boundParameter, arguments[0]);
66                 }
67                 if (argumentLength == 2) {
68                     if (@IsConstructor)
69                         return new boundFunction(boundParameter, arguments[0], arguments[1]);
70                     return boundFunction.@call(thisValue, boundParameter, arguments[0], arguments[1]);
71                 }
72                 if (argumentLength == 3) {
73                     if (@IsConstructor)
74                         return new boundFunction(boundParameter, arguments[0], arguments[1], arguments[2]);
75                     return boundFunction.@call(thisValue, boundParameter, arguments[0], arguments[1], arguments[2]);
76                 }
77                 var completeArguments = [boundParameter, ...arguments];
78                 if (!oversizedCall) {
79                     oversizedCall = function (isConstruct, boundFunction, thisValue, completeArguments) {
80                         if (isConstruct)
81                             return new boundFunction(...completeArguments);
82                         return boundFunction.@apply(thisValue, completeArguments);
83                     }
84                 }
85                 return oversizedCall(@IsConstructor, boundFunction, thisValue, completeArguments);
86             }
87             break;
88         }
89         case 2: {
90             var boundParameter1 = arguments[1];
91             var boundParameter2 = arguments[2];
92             bindingFunction = function () {
93                 if (!arguments.length) {
94                     if (@IsConstructor)
95                         return new boundFunction(boundParameter1, boundParameter2);
96                     return boundFunction.@call(thisValue, boundParameter1, boundParameter2);
97                 }
98                 if (arguments.length == 1) {
99                     if (@IsConstructor)
100                         return new boundFunction(boundParameter1, boundParameter2, arguments[0]);
101                     return boundFunction.@call(thisValue, boundParameter1, boundParameter2, arguments[0]);
102                 }
103                 if (arguments.length == 2) {
104                     if (@IsConstructor)
105                         return new boundFunction(boundParameter1, boundParameter2, arguments[0], arguments[1]);
106                     return boundFunction.@call(thisValue, boundParameter1, boundParameter2, arguments[0], arguments[1]);
107                 }
108                 if (arguments.length == 3) {
109                     if (@IsConstructor)
110                         return new boundFunction(boundParameter1, boundParameter2, arguments[0], arguments[1], arguments[2]);
111                     return boundFunction.@call(thisValue, boundParameter1, boundParameter2, arguments[0], arguments[1], arguments[2]);
112                 }
113                 var completeArguments = [boundParameter1, boundParameter2, ...arguments];
114                 if (!oversizedCall) {
115                     oversizedCall = function (isConstruct, boundFunction, thisValue, completeArguments) {
116                         if (isConstruct)
117                             return new boundFunction(...completeArguments);
118                         return boundFunction.@apply(thisValue, completeArguments);
119                     }
120                 }
121                 return oversizedCall(@IsConstructor, boundFunction, thisValue, completeArguments);
122             }
123             break;
124         }
125         case 3: {
126             var boundParameter1 = arguments[1];
127             var boundParameter2 = arguments[2];
128             var boundParameter3 = arguments[3];
129             bindingFunction = function () {
130                 if (!arguments.length) {
131                     if (@IsConstructor)
132                         return new boundFunction(boundParameter1, boundParameter2, boundParameter3);
133                     return boundFunction.@call(thisValue, boundParameter1, boundParameter2, boundParameter3);
134                 }
135                 if (arguments.length == 1) {
136                     if (@IsConstructor)
137                         return new boundFunction(boundParameter1, boundParameter2, boundParameter3, arguments[0]);
138                     return boundFunction.@call(thisValue, boundParameter1, boundParameter2, boundParameter3, arguments[0]);
139                 }
140                 if (arguments.length == 2) {
141                     if (@IsConstructor)
142                         return new boundFunction(boundParameter1, boundParameter2, boundParameter3, arguments[0], arguments[1]);
143                     return boundFunction.@call(thisValue, boundParameter1, boundParameter2, boundParameter3, arguments[0], arguments[1]);
144                 }
145                 if (arguments.length == 3) {
146                     if (@IsConstructor)
147                         return new boundFunction(boundParameter1, boundParameter2, boundParameter3, arguments[0], arguments[1], arguments[2]);
148                     return boundFunction.@call(thisValue, boundParameter1, boundParameter2, boundParameter3, arguments[0], arguments[1], arguments[2]);
149                 }
150                 var completeArguments = [boundParameter1, boundParameter2, boundParameter3, ...arguments];
151                 if (!oversizedCall) {
152                     oversizedCall = function (isConstruct, boundFunction, thisValue, completeArguments) {
153                         if (isConstruct)
154                             return new boundFunction(...completeArguments);
155                         return boundFunction.@apply(thisValue, completeArguments);
156                     }
157                 }
158                 return oversizedCall(@IsConstructor, boundFunction, thisValue, completeArguments);
159             }
160             break;
161         }
162         default:
163             var boundParameters = [];
164             for (var i = 1; i < length; i++)
165                 boundParameters[i - 1] = arguments[i];
166             
167             bindingFunction = function () {
168                 if (!arguments.length) {
169                     if (@IsConstructor)
170                         return new boundFunction(...boundParameters);
171                     return boundFunction.@apply(thisValue, boundParameters);
172                 }
173                 
174                 var completeArguments = [];
175                 var localBoundParameters = boundParameters;
176                 var boundLength = localBoundParameters.length;
177                 for (var i = 0; i < boundLength; i++)
178                     completeArguments[i] = localBoundParameters[i];
179                 for (var i = 0; i < arguments.length; i++)
180                     completeArguments[i + boundLength] = arguments[i];
181                 if (@IsConstructor)
182                     return new boundFunction(...completeArguments);
183                 else
184                     return boundFunction.@apply(thisValue, completeArguments);
185             }
186         }
187     }
188     bindingFunction.@boundFunctionName = this.name;
189     bindingFunction.@boundFunction = boundFunction.@boundFunction || boundFunction;
190     var boundLength = boundFunction.length - (arguments.length - 1);
191     if (boundLength < 0)
192         boundLength = 0;
193     bindingFunction.@boundFunctionLength = boundLength;
194     @SetTypeErrorAccessor(bindingFunction, "arguments");
195     @SetTypeErrorAccessor(bindingFunction, "caller");
196     return bindingFunction;
197 }