Generic static method automation to simplify things for callers of generic prototype methods (304828, r=mrbkap, sr=shaver).

This commit is contained in:
brendan%mozilla.org 2005-08-30 04:28:45 +00:00
parent fd98f06afb
commit 48765162d8
4 changed files with 117 additions and 37 deletions

View File

@ -3283,15 +3283,82 @@ JS_ObjectIsFunction(JSContext *cx, JSObject *obj)
return OBJ_GET_CLASS(cx, obj) == &js_FunctionClass;
}
JS_STATIC_DLL_CALLBACK(JSBool)
js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *rval)
{
jsval fsv;
JSFunctionSpec *fs;
JSObject *tmp;
if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(argv[-2]), 0, &fsv))
return JS_FALSE;
fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv);
if (argc == 0) {
/*
* Follow Function.prototype.apply and .call by using the global
* object as the 'this' param if no args. We know argv[0] is valid
* because JS_DefineFunctions, below, defined us as requiring at
* least one argument (it passes fs->nargs + 1 as the penultimate
* argument to JS_DefineFunction).
*/
while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
obj = tmp;
argv[0] = OBJECT_TO_JSVAL(obj);
argc = 1;
}
/*
* Copy all actual (argc) and required but missing (fs->nargs + 1 - argc)
* args down over our |this| parameter, argv[-1], which is almost always
* the class constructor object, e.g. Array. Then call the corresponding
* prototype native method with our first argument passed as |this|.
*/
memmove(argv - 1, argv, (fs->nargs + 1) * sizeof(jsval));
return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc - 1, argv, rval);
}
JS_PUBLIC_API(JSBool)
JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)
{
uintN flags;
JSObject *ctor;
JSFunction *fun;
CHECK_REQUEST(cx);
ctor = NULL;
for (; fs->name; fs++) {
fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs,
fs->flags);
flags = fs->flags;
/*
* Define a generic arity N+1 static method for the arity N prototype
* method if flags contains JSFUN_GENERIC_NATIVE.
*/
if (flags & JSFUN_GENERIC_NATIVE) {
if (!ctor) {
ctor = JS_GetConstructor(cx, obj);
if (!ctor)
return JS_FALSE;
}
flags &= ~JSFUN_GENERIC_NATIVE;
fun = JS_DefineFunction(cx, ctor, fs->name,
js_generic_native_method_dispatcher,
fs->nargs + 1, flags);
if (!fun)
return JS_FALSE;
fun->extra = fs->extra;
/*
* As jsapi.h notes, fs must point to storage that lives as long
* as fun->object lives.
*/
if (!JS_SetReservedSlot(cx, fun->object, 0, PRIVATE_TO_JSVAL(fs)))
return JS_FALSE;
}
fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags);
if (!fun)
return JS_FALSE;
fun->extra = fs->extra;

View File

@ -134,6 +134,19 @@ JS_BEGIN_EXTERN_C
#define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */
#define JSFUN_FLAGS_MASK 0xf8 /* overlay JSFUN_* attributes */
/*
* Re-use JSFUN_LAMBDA, which applies only to scripted functions, for use in
* JSFunctionSpec arrays that specify generic native prototype methods, i.e.,
* methods of a class prototype that are exposed as static methods taking an
* extra leading argument: the generic |this| parameter.
*
* If you set this flag in a JSFunctionSpec struct's flags initializer, then
* that struct must live at least as long as the native static method object
* created due to this flag by JS_DefineFunctions or JS_InitClass. Typically
* JSFunctionSpec structs are allocated in static arrays.
*/
#define JSFUN_GENERIC_NATIVE JSFUN_LAMBDA
/*
* Well-known JS values. The extern'd variables are initialized when the
* first JSContext is created by JS_NewContext (see below).

View File

@ -1718,32 +1718,32 @@ static JSFunctionSpec array_methods[] = {
/* Perl-ish methods. */
#if JS_HAS_SOME_PERL_FUN
{"join", array_join, 1,0,0},
{"reverse", array_reverse, 0,0,0},
{"sort", array_sort, 1,0,0},
{"join", array_join, 1,JSFUN_GENERIC_NATIVE,0},
{"reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE,0},
{"sort", array_sort, 1,JSFUN_GENERIC_NATIVE,0},
#endif
#if JS_HAS_MORE_PERL_FUN
{"push", array_push, 1,0,0},
{"pop", array_pop, 0,0,0},
{"shift", array_shift, 0,0,0},
{"unshift", array_unshift, 1,0,0},
{"splice", array_splice, 2,0,0},
{"push", array_push, 1,JSFUN_GENERIC_NATIVE,0},
{"pop", array_pop, 0,JSFUN_GENERIC_NATIVE,0},
{"shift", array_shift, 0,JSFUN_GENERIC_NATIVE,0},
{"unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE,0},
{"splice", array_splice, 2,JSFUN_GENERIC_NATIVE,0},
#endif
/* Python-esque sequence methods. */
#if JS_HAS_SEQUENCE_OPS
{"concat", array_concat, 1,0,0},
{"slice", array_slice, 2,0,0},
{"concat", array_concat, 1,JSFUN_GENERIC_NATIVE,0},
{"slice", array_slice, 2,JSFUN_GENERIC_NATIVE,0},
#endif
#if JS_HAS_ARRAY_EXTRAS
{"indexOf", array_indexOf, 1,0,0},
{"lastIndexOf", array_lastIndexOf, 1,0,0},
{"forEach", array_forEach, 1,0,0},
{"map", array_map, 1,0,0},
{"filter", array_filter, 1,0,0},
{"some", array_some, 1,0,0},
{"every", array_every, 1,0,0},
{"indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE,0},
{"lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE,0},
{"forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE,0},
{"map", array_map, 1,JSFUN_GENERIC_NATIVE,0},
{"filter", array_filter, 1,JSFUN_GENERIC_NATIVE,0},
{"some", array_some, 1,JSFUN_GENERIC_NATIVE,0},
{"every", array_every, 1,JSFUN_GENERIC_NATIVE,0},
#endif
{0,0,0,0,0}

View File

@ -2288,39 +2288,39 @@ str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
static JSFunctionSpec string_methods[] = {
#if JS_HAS_TOSOURCE
{"quote", str_quote, 0,0,0},
{"quote", str_quote, 0,JSFUN_GENERIC_NATIVE,0},
{js_toSource_str, str_toSource, 0,0,0},
#endif
/* Java-like methods. */
{js_toString_str, str_toString, 0,0,0},
{js_valueOf_str, str_valueOf, 0,0,0},
{"substring", str_substring, 2,0,0},
{"toLowerCase", str_toLowerCase, 0,0,0},
{"toUpperCase", str_toUpperCase, 0,0,0},
{"charAt", str_charAt, 1,0,0},
{"charCodeAt", str_charCodeAt, 1,0,0},
{"indexOf", str_indexOf, 1,0,0},
{"lastIndexOf", str_lastIndexOf, 1,0,0},
{"toLocaleLowerCase", str_toLocaleLowerCase, 0,0,0},
{"toLocaleUpperCase", str_toLocaleUpperCase, 0,0,0},
{"localeCompare", str_localeCompare, 1,0,0},
{"substring", str_substring, 2,JSFUN_GENERIC_NATIVE,0},
{"toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE,0},
{"toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE,0},
{"charAt", str_charAt, 1,JSFUN_GENERIC_NATIVE,0},
{"charCodeAt", str_charCodeAt, 1,JSFUN_GENERIC_NATIVE,0},
{"indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE,0},
{"lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE,0},
{"toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE,0},
{"toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE,0},
{"localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE,0},
/* Perl-ish methods (search is actually Python-esque). */
#if JS_HAS_REGEXPS
{"match", str_match, 1,0,2},
{"search", str_search, 1,0,0},
{"replace", str_replace, 2,0,0},
{"split", str_split, 2,0,0},
{"match", str_match, 1,JSFUN_GENERIC_NATIVE,2},
{"search", str_search, 1,JSFUN_GENERIC_NATIVE,0},
{"replace", str_replace, 2,JSFUN_GENERIC_NATIVE,0},
{"split", str_split, 2,JSFUN_GENERIC_NATIVE,0},
#endif
#if JS_HAS_PERL_SUBSTR
{"substr", str_substr, 2,0,0},
{"substr", str_substr, 2,JSFUN_GENERIC_NATIVE,0},
#endif
/* Python-esque sequence methods. */
#if JS_HAS_SEQUENCE_OPS
{"concat", str_concat, 0,0,0},
{"slice", str_slice, 0,0,0},
{"concat", str_concat, 0,JSFUN_GENERIC_NATIVE,0},
{"slice", str_slice, 0,JSFUN_GENERIC_NATIVE,0},
#endif
/* HTML string methods. */