ref: c87898e63b24d89de26f68e70df00a73cc64554d
parent: 35cc749225516c14183ffc5e8bbde35ee6a560b0
author: Tor Andersson <tor@ccxvii.net>
date: Thu Mar 6 11:48:42 EST 2014
Implement Function.prototype.bind().
--- a/jsfunction.c
+++ b/jsfunction.c
@@ -120,6 +120,85 @@
js_call(J, argc - 1);
}
+static void callbound(js_State *J, unsigned int argc)
+{
+ unsigned int i, fun, args, n;
+
+ fun = js_gettop(J);
+ js_currentfunction(J);
+ js_getproperty(J, fun, "__TargetFunction__");
+ js_getproperty(J, fun, "__BoundThis__");
+
+ args = js_gettop(J);
+ js_getproperty(J, fun, "__BoundArguments__");
+ n = js_getlength(J, args);
+ for (i = 0; i < n; ++i)
+ js_getindex(J, args, i);
+ js_remove(J, args);
+
+ for (i = 1; i <= argc; ++i)
+ js_copy(J, i);
+
+ js_call(J, n + argc);
+}
+
+static void constructbound(js_State *J, unsigned int argc)
+{
+ unsigned int i, fun, args, n;
+
+ fun = js_gettop(J);
+ js_currentfunction(J);
+ js_getproperty(J, fun, "__TargetFunction__");
+
+ args = js_gettop(J);
+ js_getproperty(J, fun, "__BoundArguments__");
+ n = js_getlength(J, args);
+ for (i = 0; i < n; ++i)
+ js_getindex(J, args, i);
+ js_remove(J, args);
+
+ for (i = 1; i <= argc; ++i)
+ js_copy(J, i);
+
+ js_construct(J, n + argc);
+}
+
+static void Fp_bind(js_State *J, unsigned int argc)
+{
+ unsigned int i, n;
+
+ if (!js_iscallable(J, 0))
+ js_typeerror(J, "not a function");
+
+ n = js_getlength(J, 0);
+ if (argc - 1 < n)
+ n -= argc - 1;
+ else
+ n = 0;
+
+ js_newcconstructor(J, callbound, constructbound, n);
+
+ /* Reuse target function's prototype for HasInstance check. */
+ js_getproperty(J, 0, "prototype");
+ js_defproperty(J, -2, "prototype", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+
+ /* target function */
+ js_copy(J, 0);
+ js_defproperty(J, -2, "__TargetFunction__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+
+ /* bound this */
+ js_copy(J, 1);
+ js_defproperty(J, -2, "__BoundThis__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+
+ /* bound arguments */
+ js_newarray(J);
+ for (i = 2; i <= argc; ++i) {
+ js_copy(J, i);
+ js_setindex(J, -2, i-2);
+ }
+ js_defproperty(J, -2, "__BoundArguments__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+}
+
void jsB_initfunction(js_State *J)
{
J->Function_prototype->u.c.function = jsB_Function_prototype;
@@ -130,6 +209,7 @@
jsB_propf(J, "toString", Fp_toString, 2);
jsB_propf(J, "apply", Fp_apply, 2);
jsB_propf(J, "call", Fp_call, 1);
+ jsB_propf(J, "bind", Fp_bind, 1);
}
js_newcconstructor(J, jsB_Function, jsB_Function, 1);
js_defglobal(J, "Function", JS_DONTENUM);
--- a/jsrun.c
+++ b/jsrun.c
@@ -307,6 +307,16 @@
}
}
+void js_remove(js_State *J, int idx)
+{
+ idx = idx < 0 ? TOP + idx : BOT + idx;
+ if (idx < BOT || idx >= TOP)
+ js_error(J, "stack error!");
+ for (;idx < TOP - 1; ++idx)
+ STACK[idx] = STACK[idx+1];
+ --TOP;
+}
+
void js_copy(js_State *J, int idx)
{
CHECKSTACK(1);