diff --git a/js/src/js.c b/js/src/js.c index d0b9ffe80a3f..bb7c7737db64 100644 --- a/js/src/js.c +++ b/js/src/js.c @@ -1032,6 +1032,40 @@ EscapeWideString(jschar *w) return enuf; } +#include + +static JSBool +ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp, + va_list *app) +{ + jsval *vp; + va_list ap; + jsdouble re, im; + + printf("entering ZZ_formatter"); + vp = *vpp; + ap = *app; + if (fromJS) { + if (!JS_ValueToNumber(cx, vp[0], &re)) + return JS_FALSE; + if (!JS_ValueToNumber(cx, vp[1], &im)) + return JS_FALSE; + *va_arg(ap, jsdouble *) = re; + *va_arg(ap, jsdouble *) = im; + } else { + re = va_arg(ap, jsdouble); + im = va_arg(ap, jsdouble); + if (!JS_NewNumberValue(cx, re, &vp[0])) + return JS_FALSE; + if (!JS_NewNumberValue(cx, im, &vp[1])) + return JS_FALSE; + } + *vpp = vp + 2; + *app = ap; + printf("leaving ZZ_formatter"); + return JS_TRUE; +} + static JSBool ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -1039,28 +1073,33 @@ ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jschar c = 0; int32 i = 0, j = 0; uint32 u = 0; - jsdouble d = 0, I = 0; + jsdouble d = 0, I = 0, re = 0, im = 0; char *s = NULL; JSString *str = NULL; jschar *w = NULL; - JSObject *obj = NULL; + JSObject *obj2 = NULL; JSFunction *fun = NULL; jsval v = JSVAL_VOID; + JSBool ok; - if (!JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofv*", - &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj, - &fun, &v)) { + if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter)) + return JS_FALSE;; + ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*", + &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2, + &fun, &v, &re, &im); + JS_RemoveArgumentFormatter(cx, "ZZ"); + if (!ok) return JS_FALSE; - } fprintf(gOutFile, "b %u, c %x (%c), i %ld, u %lu, j %ld\n", b, c, (char)c, i, u, j); fprintf(gOutFile, - "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s, v %s\n", + "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n" + "v %s, re %g, im %g\n", d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w), - JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj))), + JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))), fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "", - JS_GetStringBytes(JS_ValueToString(cx, v))); + JS_GetStringBytes(JS_ValueToString(cx, v)), re, im); return JS_TRUE; } #endif diff --git a/js/src/jsapi.c b/js/src/jsapi.c index 313d94ddf311..a4534e932471 100644 --- a/js/src/jsapi.c +++ b/js/src/jsapi.c @@ -91,6 +91,24 @@ JS_GetEmptyStringValue(JSContext *cx) return STRING_TO_JSVAL(cx->runtime->emptyString); } +static JSBool +TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS, + jsval **vpp, va_list *app) +{ + const char *format; + JSArgumentFormatMap *map; + + format = *formatp; + for (map = cx->argumentFormatMap; map; map = map->next) { + if (!strncmp(format, map->format, map->length)) { + *formatp = format + map->length; + return map->formatter(cx, format, fromJS, vpp, app); + } + } + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, format); + return JS_FALSE; +} + JS_PUBLIC_API(JSBool) JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, ...) @@ -108,7 +126,7 @@ JS_PUBLIC_API(JSBool) JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, const char *format, va_list ap) { - uintN i; + jsval *sp; JSBool required; char c; JSFunction *fun; @@ -117,7 +135,7 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, JSObject *obj; CHECK_REQUEST(cx); - i = 0; + sp = argv; required = JS_TRUE; while ((c = *format++) != '\0') { if (isspace(c)) @@ -126,7 +144,7 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, required = JS_FALSE; continue; } - if (i == argc) { + if (sp == argv + argc) { if (required) { fun = js_ValueToFunction(cx, &argv[-2], JS_FALSE); if (fun) { @@ -143,41 +161,41 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, } switch (c) { case 'b': - if (!js_ValueToBoolean(cx, argv[i], va_arg(ap, JSBool *))) + if (!js_ValueToBoolean(cx, *sp, va_arg(ap, JSBool *))) return JS_FALSE; break; case 'c': - if (!js_ValueToUint16(cx, argv[i], va_arg(ap, uint16 *))) + if (!js_ValueToUint16(cx, *sp, va_arg(ap, uint16 *))) return JS_FALSE; break; case 'i': - if (!js_ValueToECMAInt32(cx, argv[i], va_arg(ap, int32 *))) + if (!js_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *))) return JS_FALSE; break; case 'u': - if (!js_ValueToECMAUint32(cx, argv[i], va_arg(ap, uint32 *))) + if (!js_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32 *))) return JS_FALSE; break; case 'j': - if (!js_ValueToInt32(cx, argv[i], va_arg(ap, int32 *))) + if (!js_ValueToInt32(cx, *sp, va_arg(ap, int32 *))) return JS_FALSE; break; case 'd': - if (!js_ValueToNumber(cx, argv[i], va_arg(ap, jsdouble *))) + if (!js_ValueToNumber(cx, *sp, va_arg(ap, jsdouble *))) return JS_FALSE; break; case 'I': - if (!js_ValueToNumber(cx, argv[i], &d)) + if (!js_ValueToNumber(cx, *sp, &d)) return JS_FALSE; *va_arg(ap, jsdouble *) = js_DoubleToInteger(d); break; case 's': case 'S': case 'W': - str = js_ValueToString(cx, argv[i]); + str = js_ValueToString(cx, *sp); if (!str) return JS_FALSE; - argv[i] = STRING_TO_JSVAL(str); + *sp = STRING_TO_JSVAL(str); if (c == 's') *va_arg(ap, char **) = JS_GetStringBytes(str); else if (c == 'W') @@ -186,32 +204,31 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, *va_arg(ap, JSString **) = str; break; case 'o': - if (!js_ValueToObject(cx, argv[i], &obj)) + if (!js_ValueToObject(cx, *sp, &obj)) return JS_FALSE; - argv[i] = OBJECT_TO_JSVAL(obj); + *sp = OBJECT_TO_JSVAL(obj); *va_arg(ap, JSObject **) = obj; break; case 'f': - fun = js_ValueToFunction(cx, &argv[i], JS_FALSE); + fun = js_ValueToFunction(cx, sp, JS_FALSE); if (!fun) return JS_FALSE; - argv[i] = OBJECT_TO_JSVAL(fun->object); + *sp = OBJECT_TO_JSVAL(fun->object); *va_arg(ap, JSFunction **) = fun; break; case 'v': - *va_arg(ap, jsval *) = argv[i]; + *va_arg(ap, jsval *) = *sp; break; case '*': break; - default: { - char charBuf[2] = " "; - charBuf[0] = c; - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, - charBuf); - return JS_FALSE; - } + default: + format--; + if (!TryArgumentFormatter(cx, &format, JS_TRUE, &sp, &ap)) + return JS_FALSE; + /* NB: the formatter already updated sp, so we continue here. */ + continue; } - i++; + sp++; } return JS_TRUE; } @@ -300,13 +317,12 @@ JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap) case 'v': *sp = va_arg(ap, jsval); break; - default: { - char charBuf[2] = " "; - charBuf[0] = c; - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, - charBuf); - goto bad; - } + default: + format--; + if (!TryArgumentFormatter(cx, &format, JS_FALSE, &sp, &ap)) + goto bad; + /* NB: the formatter already updated sp, so we continue here. */ + continue; } sp++; } @@ -324,6 +340,53 @@ JS_PopArguments(JSContext *cx, void *mark) js_FreeStack(cx, mark); } +JS_PUBLIC_API(JSBool) +JS_AddArgumentFormatter(JSContext *cx, const char *format, + JSArgumentFormatter formatter) +{ + size_t length; + JSArgumentFormatMap **mpp, *map; + + length = strlen(format); + mpp = &cx->argumentFormatMap; + while ((map = *mpp) != NULL) { + /* Insert before any shorter string to match before prefixes. */ + if (map->length < length) + break; + if (map->length == length && !strcmp(map->format, format)) + goto out; + mpp = &map->next; + } + map = JS_malloc(cx, sizeof *map); + if (!map) + return JS_FALSE; + map->format = format; + map->length = length; + map->next = *mpp; + *mpp = map; +out: + map->formatter = formatter; + return JS_TRUE; +} + +JS_PUBLIC_API(void) +JS_RemoveArgumentFormatter(JSContext *cx, const char *format) +{ + size_t length; + JSArgumentFormatMap **mpp, *map; + + length = strlen(format); + mpp = &cx->argumentFormatMap; + while ((map = *mpp) != NULL) { + if (map->length == length && !strcmp(map->format, format)) { + *mpp = map->next; + JS_free(cx, map); + return; + } + mpp = &map->next; + } +} + JS_PUBLIC_API(JSBool) JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 6bbf081d2ba9..0049b4438b5e 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -192,6 +192,54 @@ JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap); extern JS_PUBLIC_API(void) JS_PopArguments(JSContext *cx, void *mark); +#ifdef va_start +/* + * Add and remove a format string handler for JS_{Convert,Push}Arguments{,VA}. + * The handler function has this signature (see jspubtd.h): + * + * JSBool MyArgumentFormatter(JSContext *cx, const char *format, + * JSBool fromJS, jsval **vpp, va_list *app); + * + * It should return true on success, and return false after reporting an error + * or detecting an already-reported error. + * + * For a given format string, for example "AA", the formatter is called from + * JS_ConvertArgumentsVA like so: + * + * formatter(cx, "AA...", JS_TRUE, &sp, &ap); + * + * sp points into the arguments array on the JS stack, while ap points into + * the stdarg.h va_list on the C stack. The JS_TRUE passed for fromJS tells + * the formatter to convert zero or more jsvals at sp to zero or more C values + * accessed via pointers-to-values at ap, updating both sp (via *vpp) and ap + * (via *app) to point past the converted arguments and their result pointers + * on the C stack. + * + * When called from JS_PushArgumentsVA, the formatter is invoked thus: + * + * formatter(cx, "AA...", JS_FALSE, &sp, &ap); + * + * where JS_FALSE for fromJS means to wrap the C values at ap according to the + * format specifier and store them at sp, updating ap and sp appropriately. + * + * The "..." after "AA" is the rest of the format string that was passed into + * JS_{Convert,Push}Arguments{,VA}. The actual format trailing substring used + * in each Convert or PushArguments call is passed to the formatter, so that + * one such function may implement several formats, in order to share code. + * + * Remove just forgets about any handler associated with format. Add does not + * copy format, it points at the string storage allocated by the caller, which + * is typically a string constant. If format is in dynamic storage, it is up + * to the caller to keep the string alive until Remove is called. + */ +JS_PUBLIC_API(JSBool) +JS_AddArgumentFormatter(JSContext *cx, const char *format, + JSArgumentFormatter formatter); + +JS_PUBLIC_API(void) +JS_RemoveArgumentFormatter(JSContext *cx, const char *format); +#endif /* va_start */ + extern JS_PUBLIC_API(JSBool) JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index aa69e1f357e5..0aafa9f3386e 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -117,6 +117,20 @@ struct JSRuntime { #endif }; +#ifdef va_start +/* + * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to + * formatter functions. Elements are sorted in non-increasing format string + * length order. + */ +struct JSArgumentFormatMap { + const char *format; + size_t length; + JSArgumentFormatter formatter; + JSArgumentFormatMap *next; +}; +#endif + struct JSContext { JSCList links; @@ -151,6 +165,9 @@ struct JSContext { /* State for object and array toSource conversion. */ JSSharpObjectMap sharpObjectMap; + /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */ + JSArgumentFormatMap *argumentFormatMap; + /* Last message string and trace file for debugging. */ char *lastMessage; #ifdef DEBUG diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index df886d87c521..f79cf41a6aee 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -41,33 +41,34 @@ typedef uint8 jssrcnote; typedef uint32 jsatomid; /* Struct typedefs. */ -typedef struct JSCodeGenerator JSCodeGenerator; -typedef struct JSGCThing JSGCThing; -typedef struct JSParseNode JSParseNode; -typedef struct JSSharpObjectMap JSSharpObjectMap; -typedef struct JSToken JSToken; -typedef struct JSTokenPos JSTokenPos; -typedef struct JSTokenPtr JSTokenPtr; -typedef struct JSTokenStream JSTokenStream; -typedef struct JSTreeContext JSTreeContext; -typedef struct JSTryNote JSTryNote; +typedef struct JSArgumentFormatMap JSArgumentFormatMap; +typedef struct JSCodeGenerator JSCodeGenerator; +typedef struct JSGCThing JSGCThing; +typedef struct JSParseNode JSParseNode; +typedef struct JSSharpObjectMap JSSharpObjectMap; +typedef struct JSToken JSToken; +typedef struct JSTokenPos JSTokenPos; +typedef struct JSTokenPtr JSTokenPtr; +typedef struct JSTokenStream JSTokenStream; +typedef struct JSTreeContext JSTreeContext; +typedef struct JSTryNote JSTryNote; /* Friend "Advanced API" typedefs. */ -typedef struct JSAtom JSAtom; -typedef struct JSAtomList JSAtomList; -typedef struct JSAtomListElement JSAtomListElement; -typedef struct JSAtomMap JSAtomMap; -typedef struct JSAtomState JSAtomState; -typedef struct JSCodeSpec JSCodeSpec; -typedef struct JSPrinter JSPrinter; -typedef struct JSRegExp JSRegExp; -typedef struct JSRegExpStatics JSRegExpStatics; -typedef struct JSScope JSScope; -typedef struct JSScopeOps JSScopeOps; -typedef struct JSScopeProperty JSScopeProperty; -typedef struct JSStackFrame JSStackFrame; -typedef struct JSSubString JSSubString; -typedef struct JSSymbol JSSymbol; +typedef struct JSAtom JSAtom; +typedef struct JSAtomList JSAtomList; +typedef struct JSAtomListElement JSAtomListElement; +typedef struct JSAtomMap JSAtomMap; +typedef struct JSAtomState JSAtomState; +typedef struct JSCodeSpec JSCodeSpec; +typedef struct JSPrinter JSPrinter; +typedef struct JSRegExp JSRegExp; +typedef struct JSRegExpStatics JSRegExpStatics; +typedef struct JSScope JSScope; +typedef struct JSScopeOps JSScopeOps; +typedef struct JSScopeProperty JSScopeProperty; +typedef struct JSStackFrame JSStackFrame; +typedef struct JSSubString JSSubString; +typedef struct JSSymbol JSSymbol; /* "Friend" types used by jscntxt.h and jsdbgapi.h. */ typedef enum JSTrapStatus { diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 3f06c37f2806..05561c669386 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -238,6 +238,12 @@ typedef const JSErrorFormatString * (* CRT_CALL JSErrorCallback)(void *userRef, const char *locale, const uintN errorNumber); +#ifdef va_start +typedef JSBool +(* CRT_CALL JSArgumentFormatter)(JSContext *cx, const char *format, + JSBool fromJS, jsval **vpp, va_list *app); +#endif + JS_END_EXTERN_C #endif /* jspubtd_h___ */