Added custom JS_{Convert,Push}Arguments{,VA} formatter function support for

use by XPConnect and other clients.
This commit is contained in:
brendan%netscape.com 1999-06-28 03:13:21 +00:00
Родитель 6501c344cd
Коммит e3df4f3ab2
6 изменённых файлов: 240 добавлений и 66 удалений

Просмотреть файл

@ -1032,6 +1032,40 @@ EscapeWideString(jschar *w)
return enuf; return enuf;
} }
#include <stdarg.h>
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 static JSBool
ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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; jschar c = 0;
int32 i = 0, j = 0; int32 i = 0, j = 0;
uint32 u = 0; uint32 u = 0;
jsdouble d = 0, I = 0; jsdouble d = 0, I = 0, re = 0, im = 0;
char *s = NULL; char *s = NULL;
JSString *str = NULL; JSString *str = NULL;
jschar *w = NULL; jschar *w = NULL;
JSObject *obj = NULL; JSObject *obj2 = NULL;
JSFunction *fun = NULL; JSFunction *fun = NULL;
jsval v = JSVAL_VOID; jsval v = JSVAL_VOID;
JSBool ok;
if (!JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofv*", if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
&b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj, return JS_FALSE;;
&fun, &v)) { 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; return JS_FALSE;
}
fprintf(gOutFile, fprintf(gOutFile,
"b %u, c %x (%c), i %ld, u %lu, j %ld\n", "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
b, c, (char)c, i, u, j); b, c, (char)c, i, u, j);
fprintf(gOutFile, 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), 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)) : "", 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; return JS_TRUE;
} }
#endif #endif

Просмотреть файл

@ -91,6 +91,24 @@ JS_GetEmptyStringValue(JSContext *cx)
return STRING_TO_JSVAL(cx->runtime->emptyString); 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_PUBLIC_API(JSBool)
JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, 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, JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv,
const char *format, va_list ap) const char *format, va_list ap)
{ {
uintN i; jsval *sp;
JSBool required; JSBool required;
char c; char c;
JSFunction *fun; JSFunction *fun;
@ -117,7 +135,7 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv,
JSObject *obj; JSObject *obj;
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
i = 0; sp = argv;
required = JS_TRUE; required = JS_TRUE;
while ((c = *format++) != '\0') { while ((c = *format++) != '\0') {
if (isspace(c)) if (isspace(c))
@ -126,7 +144,7 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv,
required = JS_FALSE; required = JS_FALSE;
continue; continue;
} }
if (i == argc) { if (sp == argv + argc) {
if (required) { if (required) {
fun = js_ValueToFunction(cx, &argv[-2], JS_FALSE); fun = js_ValueToFunction(cx, &argv[-2], JS_FALSE);
if (fun) { if (fun) {
@ -143,41 +161,41 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv,
} }
switch (c) { switch (c) {
case 'b': case 'b':
if (!js_ValueToBoolean(cx, argv[i], va_arg(ap, JSBool *))) if (!js_ValueToBoolean(cx, *sp, va_arg(ap, JSBool *)))
return JS_FALSE; return JS_FALSE;
break; break;
case 'c': case 'c':
if (!js_ValueToUint16(cx, argv[i], va_arg(ap, uint16 *))) if (!js_ValueToUint16(cx, *sp, va_arg(ap, uint16 *)))
return JS_FALSE; return JS_FALSE;
break; break;
case 'i': case 'i':
if (!js_ValueToECMAInt32(cx, argv[i], va_arg(ap, int32 *))) if (!js_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *)))
return JS_FALSE; return JS_FALSE;
break; break;
case 'u': case 'u':
if (!js_ValueToECMAUint32(cx, argv[i], va_arg(ap, uint32 *))) if (!js_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32 *)))
return JS_FALSE; return JS_FALSE;
break; break;
case 'j': case 'j':
if (!js_ValueToInt32(cx, argv[i], va_arg(ap, int32 *))) if (!js_ValueToInt32(cx, *sp, va_arg(ap, int32 *)))
return JS_FALSE; return JS_FALSE;
break; break;
case 'd': case 'd':
if (!js_ValueToNumber(cx, argv[i], va_arg(ap, jsdouble *))) if (!js_ValueToNumber(cx, *sp, va_arg(ap, jsdouble *)))
return JS_FALSE; return JS_FALSE;
break; break;
case 'I': case 'I':
if (!js_ValueToNumber(cx, argv[i], &d)) if (!js_ValueToNumber(cx, *sp, &d))
return JS_FALSE; return JS_FALSE;
*va_arg(ap, jsdouble *) = js_DoubleToInteger(d); *va_arg(ap, jsdouble *) = js_DoubleToInteger(d);
break; break;
case 's': case 's':
case 'S': case 'S':
case 'W': case 'W':
str = js_ValueToString(cx, argv[i]); str = js_ValueToString(cx, *sp);
if (!str) if (!str)
return JS_FALSE; return JS_FALSE;
argv[i] = STRING_TO_JSVAL(str); *sp = STRING_TO_JSVAL(str);
if (c == 's') if (c == 's')
*va_arg(ap, char **) = JS_GetStringBytes(str); *va_arg(ap, char **) = JS_GetStringBytes(str);
else if (c == 'W') else if (c == 'W')
@ -186,32 +204,31 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv,
*va_arg(ap, JSString **) = str; *va_arg(ap, JSString **) = str;
break; break;
case 'o': case 'o':
if (!js_ValueToObject(cx, argv[i], &obj)) if (!js_ValueToObject(cx, *sp, &obj))
return JS_FALSE; return JS_FALSE;
argv[i] = OBJECT_TO_JSVAL(obj); *sp = OBJECT_TO_JSVAL(obj);
*va_arg(ap, JSObject **) = obj; *va_arg(ap, JSObject **) = obj;
break; break;
case 'f': case 'f':
fun = js_ValueToFunction(cx, &argv[i], JS_FALSE); fun = js_ValueToFunction(cx, sp, JS_FALSE);
if (!fun) if (!fun)
return JS_FALSE; return JS_FALSE;
argv[i] = OBJECT_TO_JSVAL(fun->object); *sp = OBJECT_TO_JSVAL(fun->object);
*va_arg(ap, JSFunction **) = fun; *va_arg(ap, JSFunction **) = fun;
break; break;
case 'v': case 'v':
*va_arg(ap, jsval *) = argv[i]; *va_arg(ap, jsval *) = *sp;
break; break;
case '*': case '*':
break; break;
default: { default:
char charBuf[2] = " "; format--;
charBuf[0] = c; if (!TryArgumentFormatter(cx, &format, JS_TRUE, &sp, &ap))
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, return JS_FALSE;
charBuf); /* NB: the formatter already updated sp, so we continue here. */
return JS_FALSE; continue;
}
} }
i++; sp++;
} }
return JS_TRUE; return JS_TRUE;
} }
@ -300,13 +317,12 @@ JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap)
case 'v': case 'v':
*sp = va_arg(ap, jsval); *sp = va_arg(ap, jsval);
break; break;
default: { default:
char charBuf[2] = " "; format--;
charBuf[0] = c; if (!TryArgumentFormatter(cx, &format, JS_FALSE, &sp, &ap))
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, goto bad;
charBuf); /* NB: the formatter already updated sp, so we continue here. */
goto bad; continue;
}
} }
sp++; sp++;
} }
@ -324,6 +340,53 @@ JS_PopArguments(JSContext *cx, void *mark)
js_FreeStack(cx, 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_PUBLIC_API(JSBool)
JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp)
{ {

Просмотреть файл

@ -192,6 +192,54 @@ JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap);
extern JS_PUBLIC_API(void) extern JS_PUBLIC_API(void)
JS_PopArguments(JSContext *cx, void *mark); 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) extern JS_PUBLIC_API(JSBool)
JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp); JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp);

Просмотреть файл

@ -117,6 +117,20 @@ struct JSRuntime {
#endif #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 { struct JSContext {
JSCList links; JSCList links;
@ -151,6 +165,9 @@ struct JSContext {
/* State for object and array toSource conversion. */ /* State for object and array toSource conversion. */
JSSharpObjectMap sharpObjectMap; JSSharpObjectMap sharpObjectMap;
/* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */
JSArgumentFormatMap *argumentFormatMap;
/* Last message string and trace file for debugging. */ /* Last message string and trace file for debugging. */
char *lastMessage; char *lastMessage;
#ifdef DEBUG #ifdef DEBUG

Просмотреть файл

@ -41,33 +41,34 @@ typedef uint8 jssrcnote;
typedef uint32 jsatomid; typedef uint32 jsatomid;
/* Struct typedefs. */ /* Struct typedefs. */
typedef struct JSCodeGenerator JSCodeGenerator; typedef struct JSArgumentFormatMap JSArgumentFormatMap;
typedef struct JSGCThing JSGCThing; typedef struct JSCodeGenerator JSCodeGenerator;
typedef struct JSParseNode JSParseNode; typedef struct JSGCThing JSGCThing;
typedef struct JSSharpObjectMap JSSharpObjectMap; typedef struct JSParseNode JSParseNode;
typedef struct JSToken JSToken; typedef struct JSSharpObjectMap JSSharpObjectMap;
typedef struct JSTokenPos JSTokenPos; typedef struct JSToken JSToken;
typedef struct JSTokenPtr JSTokenPtr; typedef struct JSTokenPos JSTokenPos;
typedef struct JSTokenStream JSTokenStream; typedef struct JSTokenPtr JSTokenPtr;
typedef struct JSTreeContext JSTreeContext; typedef struct JSTokenStream JSTokenStream;
typedef struct JSTryNote JSTryNote; typedef struct JSTreeContext JSTreeContext;
typedef struct JSTryNote JSTryNote;
/* Friend "Advanced API" typedefs. */ /* Friend "Advanced API" typedefs. */
typedef struct JSAtom JSAtom; typedef struct JSAtom JSAtom;
typedef struct JSAtomList JSAtomList; typedef struct JSAtomList JSAtomList;
typedef struct JSAtomListElement JSAtomListElement; typedef struct JSAtomListElement JSAtomListElement;
typedef struct JSAtomMap JSAtomMap; typedef struct JSAtomMap JSAtomMap;
typedef struct JSAtomState JSAtomState; typedef struct JSAtomState JSAtomState;
typedef struct JSCodeSpec JSCodeSpec; typedef struct JSCodeSpec JSCodeSpec;
typedef struct JSPrinter JSPrinter; typedef struct JSPrinter JSPrinter;
typedef struct JSRegExp JSRegExp; typedef struct JSRegExp JSRegExp;
typedef struct JSRegExpStatics JSRegExpStatics; typedef struct JSRegExpStatics JSRegExpStatics;
typedef struct JSScope JSScope; typedef struct JSScope JSScope;
typedef struct JSScopeOps JSScopeOps; typedef struct JSScopeOps JSScopeOps;
typedef struct JSScopeProperty JSScopeProperty; typedef struct JSScopeProperty JSScopeProperty;
typedef struct JSStackFrame JSStackFrame; typedef struct JSStackFrame JSStackFrame;
typedef struct JSSubString JSSubString; typedef struct JSSubString JSSubString;
typedef struct JSSymbol JSSymbol; typedef struct JSSymbol JSSymbol;
/* "Friend" types used by jscntxt.h and jsdbgapi.h. */ /* "Friend" types used by jscntxt.h and jsdbgapi.h. */
typedef enum JSTrapStatus { typedef enum JSTrapStatus {

Просмотреть файл

@ -238,6 +238,12 @@ typedef const JSErrorFormatString *
(* CRT_CALL JSErrorCallback)(void *userRef, const char *locale, (* CRT_CALL JSErrorCallback)(void *userRef, const char *locale,
const uintN errorNumber); 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 JS_END_EXTERN_C
#endif /* jspubtd_h___ */ #endif /* jspubtd_h___ */