Bug 738356. Add JSAPI for doing the equivalent of Function.prototype.bind. r=jorendorff

This commit is contained in:
Boris Zbarsky 2012-04-05 13:14:52 -04:00
parent de34d6a9c5
commit c251fe869d
6 changed files with 68 additions and 9 deletions

View File

@ -93,6 +93,7 @@ CPPSRCS = \
testValueABI.cpp \
testVersion.cpp \
testXDR.cpp \
testBindCallable.cpp \
$(NULL)
CSRCS = \

View File

@ -0,0 +1,27 @@
#include "tests.h"
BEGIN_TEST(test_BindCallable)
{
jsval v;
EVAL("({ somename : 1717 })", &v);
CHECK(JSVAL_IS_OBJECT(v));
jsval func;
EVAL("(function() { return this.somename; })", &func);
CHECK(JSVAL_IS_OBJECT(func));
JSObject* newCallable = JS_BindCallable(cx, JSVAL_TO_OBJECT(func),
JSVAL_TO_OBJECT(v));
CHECK(newCallable);
jsval retval;
bool called = JS_CallFunctionValue(cx, NULL, OBJECT_TO_JSVAL(newCallable),
0, NULL, &retval);
CHECK(called);
CHECK(JSVAL_IS_INT(retval));
CHECK(JSVAL_TO_INT(retval) == 1717);
return true;
}
END_TEST(test_BindCallable)

View File

@ -4631,6 +4631,14 @@ JS_IsNativeFunction(JSObject *funobj, JSNative call)
return fun->isNative() && fun->native() == call;
}
JS_PUBLIC_API(JSObject*)
JS_BindCallable(JSContext *cx, JSObject *callable, JSObject *newThis)
{
RootedVarObject target(cx);
target = callable;
return js_fun_bind(cx, target, ObjectValue(*newThis), NULL, 0);
}
JSBool
js_generic_native_method_dispatcher(JSContext *cx, unsigned argc, Value *vp)
{

View File

@ -4321,6 +4321,14 @@ JS_ObjectIsCallable(JSContext *cx, JSObject *obj);
extern JS_PUBLIC_API(JSBool)
JS_IsNativeFunction(JSObject *funobj, JSNative call);
/*
* Bind the given callable to use the given object as "this".
*
* If |callable| is not callable, will throw and return NULL.
*/
extern JS_PUBLIC_API(JSObject*)
JS_BindCallable(JSContext *cx, JSObject *callable, JSObject *newThis);
extern JS_PUBLIC_API(JSBool)
JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs);

View File

@ -895,6 +895,22 @@ fun_bind(JSContext *cx, unsigned argc, Value *vp)
argslen = args.length() - 1;
}
/* Steps 7-9. */
Value thisArg = args.length() >= 1 ? args[0] : UndefinedValue();
JSObject *boundFunction = js_fun_bind(cx, target, thisArg, boundArgs, argslen);
if (!boundFunction)
return false;
/* Step 22. */
args.rval().setObject(*boundFunction);
return true;
}
JSObject*
js_fun_bind(JSContext *cx, HandleObject target, Value thisArg,
Value *boundArgs, unsigned argslen)
{
/* Steps 15-16. */
unsigned length = 0;
if (target->isFunction()) {
@ -910,23 +926,18 @@ fun_bind(JSContext *cx, unsigned argc, Value *vp)
js_NewFunction(cx, NULL, CallOrConstructBoundFunction, length,
JSFUN_CONSTRUCTOR, target, name);
if (!funobj)
return false;
return NULL;
/* NB: Bound functions abuse |parent| to store their target. */
if (!funobj->setParent(cx, target))
return false;
return NULL;
/* Steps 7-9. */
Value thisArg = args.length() >= 1 ? args[0] : UndefinedValue();
if (!funobj->toFunction()->initBoundFunction(cx, thisArg, boundArgs, argslen))
return false;
return NULL;
/* Steps 17, 19-21 are handled by fun_resolve. */
/* Step 18 is the default for new functions. */
/* Step 22. */
args.rval().setObject(*funobj);
return true;
return funobj;
}
/*

View File

@ -317,4 +317,8 @@ js_fun_apply(JSContext *cx, unsigned argc, js::Value *vp);
extern JSBool
js_fun_call(JSContext *cx, unsigned argc, js::Value *vp);
extern JSObject*
js_fun_bind(JSContext *cx, js::HandleObject target, js::Value thisArg,
js::Value *boundArgs, unsigned argslen);
#endif /* jsfun_h___ */