From b32b6c20e8ccd1c25d883badddaab435411cfc5b Mon Sep 17 00:00:00 2001 From: "mccabe%netscape.com" Date: Fri, 31 Jul 1998 00:07:22 +0000 Subject: [PATCH] Propagating numerous fixes from js/ref and development branches, including but not limited to: - Preliminary exception handling per ECMA proposal; try, multiple catchblocks, and finally. Catchblocks are of the form catch (v) or catch(v:), where guard is an optional boolean expression that is evaluated to determine whether the exception is to be caught by that block. - ECMA-proposed 'in' operator; "'foo' in o" or "4 in o" asks if o has property foo or element 4. - Added a new set of defines in jsconfig.h for js 1.4 features-in-progress. (in, instanceof, exception handling.) Default build version is now 1.4. Fixed a few conditional features that had become broken. - Progress towards porting to FreeBSD and Alpha; casts of NaN and friends to int are a little more localized. Not there yet... - New config files to compile on more OSes; various fixes to improve portability. git-svn-id: svn://10.0.0.236/trunk@6907 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/js/src/js.c | 191 ++++++++++++---- mozilla/js/src/jsapi.c | 73 +++--- mozilla/js/src/jsapi.h | 9 +- mozilla/js/src/jsarray.c | 76 ++++--- mozilla/js/src/jsatom.c | 14 +- mozilla/js/src/jscntxt.h | 13 +- mozilla/js/src/jsconfig.h | 52 ++++- mozilla/js/src/jsdate.c | 11 +- mozilla/js/src/jsdbgapi.c | 10 + mozilla/js/src/jsdbgapi.h | 5 + mozilla/js/src/jsemit.c | 406 +++++++++++++++++++++++++-------- mozilla/js/src/jsemit.h | 17 +- mozilla/js/src/jsfun.c | 69 ++++-- mozilla/js/src/jsfun.h | 12 +- mozilla/js/src/jsgc.c | 20 +- mozilla/js/src/jsinterp.c | 216 ++++++++++++------ mozilla/js/src/jsinterp.h | 5 +- mozilla/js/src/jslock.c | 55 ++--- mozilla/js/src/jsmath.c | 11 +- mozilla/js/src/jsnum.c | 377 ++++++++++++++++++------------- mozilla/js/src/jsnum.h | 36 ++- mozilla/js/src/jsobj.c | 173 ++++++++++---- mozilla/js/src/jsopcode.c | 72 ++++-- mozilla/js/src/jsopcode.def | 10 + mozilla/js/src/jsparse.c | 438 +++++++++++++++++++++++------------- mozilla/js/src/jspubtd.h | 7 +- mozilla/js/src/jsregexp.c | 16 ++ mozilla/js/src/jsscan.c | 44 ++-- mozilla/js/src/jsscan.h | 1 + mozilla/js/src/jsscript.h | 38 ++-- mozilla/js/src/jsstr.c | 17 +- mozilla/js/src/jsstr.h | 6 + mozilla/js/src/jsxdrapi.c | 22 +- mozilla/js/src/jsxdrapi.h | 2 +- mozilla/js/src/prconv.sed | Bin 738 -> 738 bytes 35 files changed, 1749 insertions(+), 775 deletions(-) diff --git a/mozilla/js/src/js.c b/mozilla/js/src/js.c index d0be0e7a63a..a45bfefc6e1 100644 --- a/mozilla/js/src/js.c +++ b/mozilla/js/src/js.c @@ -46,6 +46,10 @@ #include "jsscope.h" #include "jsscript.h" +#ifdef PERLCONNECT +#include "jsperl.h" +#endif + #ifdef LIVECONNECT #include "jsjava.h" #endif @@ -55,6 +59,9 @@ #ifdef JSDEBUGGER_JAVA_UI #include "jsdjava.h" #endif /* JSDEBUGGER_JAVA_UI */ +#ifdef JSDEBUGGER_C_UI +#include "jsdb.h" +#endif /* JSDEBUGGER_C_UI */ #endif /* JSDEBUGGER */ #ifdef XP_UNIX @@ -66,6 +73,28 @@ #ifdef XP_MAC #define isatty(f) 1 + +#include +#include + +static char* mac_argv[] = { "js", NULL }; + +static void initConsole(StringPtr consoleName, const char* startupMessage, int *argc, char** *argv) +{ + SIOUXSettings.autocloseonquit = true; + SIOUXSettings.asktosaveonclose = false; + /* SIOUXSettings.initializeTB = false; + SIOUXSettings.showstatusline = true;*/ + puts(startupMessage); + SIOUXSetTitle(consoleName); + + /* set up a buffer for stderr (otherwise it's a pig). */ + setvbuf(stderr, malloc(BUFSIZ), _IOLBF, BUFSIZ); + + *argc = 1; + *argv = mac_argv; +} + #endif #ifndef JSFILE @@ -109,7 +138,7 @@ Process(JSContext *cx, JSObject *obj, char *filename) while((ch = fgetc(ts->file)) != EOF) { if(ch == '\n' || ch == '\r') break; - } + } } ungetc(ch, ts->file); } @@ -151,6 +180,82 @@ out: PR_FreeArenaPool(&cx->tempPool); } +static int +usage(void) +{ + fprintf(stderr, "%s\n", JS_GetImplementationVersion()); + fprintf(stderr, "usage: js [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n"); + return 2; +} + +static int +ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) +{ + int i; + char *filename = NULL; + jsint length; + jsval *vector; + jsval *p; + JSObject *argsObj; + + for (i=0; i < argc; i++) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 'v': + if (i+1 == argc) { + return usage(); + } + JS_SetVersion(cx, atoi(argv[i+1])); + i++; + break; + + case 'f': + if (i+1 == argc) { + return usage(); + } + filename = argv[i+1]; + /* "-f -" means read from stdin */ + if (filename[0] == '-' && filename[1] == '\0') + filename = NULL; + Process(cx, obj, filename); + i++; + break; + default: + return usage(); + } + } else { + filename = argv[i++]; + break; + } + } + + length = argc - i; + vector = JS_malloc(cx, length * sizeof(jsval)); + p = vector; + + if (vector == NULL) + return 1; + + while (i < argc) { + JSString *str = JS_NewStringCopyZ(cx, argv[i]); + if (str == NULL) + return 1; + *p++ = STRING_TO_JSVAL(str); + i++; + } + argsObj = JS_NewArrayObject(cx, length, vector); + if (argsObj == NULL) + return 1; + + if (!JS_DefineProperty(cx, obj, "arguments", + OBJECT_TO_JSVAL(argsObj), NULL, NULL, 0)) + return 1; + + Process(cx, obj, filename); + return 0; +} + + static JSBool Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -161,6 +266,9 @@ Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_TRUE; } +static void +my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report); + static JSBool Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -179,13 +287,8 @@ Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) filename = JS_GetStringBytes(str); errno = 0; script = JS_CompileFile(cx, obj, filename); - if (!script) { - fprintf(stderr, "js: cannot load %s", filename); - if (errno) - fprintf(stderr, ": %s", strerror(errno)); - putc('\n', stderr); + if (!script) continue; - } ok = JS_ExecuteScript(cx, obj, script, &result); JS_DestroyScript(cx, script); if (!ok) @@ -391,7 +494,7 @@ PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #ifdef DEBUG static void -SingleNote(JSContext *cx, JSFunction *fun ) +SrcNotes(JSContext *cx, JSFunction *fun ) { uintN offset, delta; jssrcnote *notes, *sn; @@ -432,6 +535,11 @@ SingleNote(JSContext *cx, JSFunction *fun ) atom = js_GetAtom(cx, &fun->script->atomMap, atomIndex); printf(" atom %u (%s)", (uintN)atomIndex, ATOM_BYTES(atom)); break; + case SRC_CATCH: + delta = (uintN) js_GetSrcNoteOffset(sn, 0); + if (delta) + printf(" guard size %u", delta); + break; default:; } putchar('\n'); @@ -450,23 +558,23 @@ Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!fun) return JS_FALSE; - SingleNote(cx, fun); + SrcNotes(cx, fun); } return JS_TRUE; } static JSBool -ExceptionTable(JSContext *cx, JSFunction *fun) +TryNotes(JSContext *cx, JSFunction *fun) { - JSTryNote *iter = fun->script->trynotes; + JSTryNote *tn = fun->script->trynotes; - if (!iter) + if (!tn) return JS_TRUE; - printf("\nException table:\nstart\tend\tcatch\tfinally\n"); - while (iter->start && iter->end) { - printf(" %d\t%d\t%d\t%d\n", - iter->start, iter->end, iter->catch, iter->finally); - iter++; + printf("\nException table:\nstart\tend\tcatch\n"); + while (tn->start && tn->catchStart) { + printf(" %d\t%d\t%d\n", + tn->start, tn->length, tn->catchStart); + tn++; } return JS_TRUE; } @@ -492,8 +600,8 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_FALSE; js_Disassemble(cx, fun->script, lines, stdout); - SingleNote(cx, fun); - ExceptionTable(cx, fun); + SrcNotes(cx, fun); + TryNotes(cx, fun); } return JS_TRUE; } @@ -857,6 +965,7 @@ Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSString *str; const char *bytes; + printf("%s\n", JS_GetImplementationVersion()); if (argc == 0) { ShowHelpHeader(); for (i = 0; shell_functions[i].name; i++) @@ -1046,7 +1155,7 @@ my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) fputs("^\n", stderr); } -#if defined DEBUG && defined XP_UNIX +#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX) static JSBool Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -1099,7 +1208,7 @@ Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool global_resolve(JSContext *cx, JSObject *obj, jsval id) { -#if defined DEBUG && defined XP_UNIX +#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX) /* * Do this expensive hack only for unoptimized Unix builds, which are not * used for benchmarking. @@ -1155,11 +1264,11 @@ static JSClass global_class = { int main(int argc, char **argv) { - int c, i; JSVersion version; JSRuntime *rt; JSContext *cx; JSObject *glob, *it; + int result; #ifdef XP_OS2 /* these streams are normally line buffered on OS/2 and need a \n, * @@ -1168,26 +1277,15 @@ main(int argc, char **argv) setbuf(stderr,0); #endif +#ifdef XP_MAC + initConsole("\pJavaScript Shell", "Welcome to js shell.", &argc, &argv); +#endif + version = JSVERSION_DEFAULT; -#ifdef XP_UNIX - while ((c = getopt(argc, argv, "v:")) != -1) { - switch (c) { - case 'v': - version = atoi(optarg); - break; - default: - fprintf(stderr, "usage: js [-v version]\n"); - return 2; - } - } - argc -= optind; - argv += optind; -#else - c = -1; +#ifndef LIVECONNECT argc--; argv++; #endif - rt = JS_NewRuntime(8L * 1024L * 1024L); if (!rt) return 1; @@ -1214,6 +1312,11 @@ main(int argc, char **argv) if (!JS_DefineProperties(cx, it, its_props)) return 1; +#ifdef PERLCONNECT + if (!js_InitPerlClass(cx, glob)) + return 1; +#endif + #ifdef LIVECONNECT if (!JSJ_SimpleInit(cx, glob, NULL, getenv("CLASSPATH"))) return 1; @@ -1240,14 +1343,12 @@ main(int argc, char **argv) * is passed on the cmd line. */ #endif /* JSDEBUGGER_JAVA_UI */ +#ifdef JSDEBUGGER_C_UI + JSDB_InitDebugger(rt, _jsdc, 0); +#endif /* JSDEBUGGER_C_UI */ #endif /* JSDEBUGGER */ - if (argc > 0) { - for (i = 0; i < argc; i++) - Process(cx, glob, argv[i]); - } else { - Process(cx, glob, NULL); - } + result = ProcessArgs(cx, glob, argv, argc); #ifdef JSDEBUGGER if (_jsdc) @@ -1257,5 +1358,5 @@ main(int argc, char **argv) JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); - return 0; + return result; } diff --git a/mozilla/js/src/jsapi.c b/mozilla/js/src/jsapi.c index 406e2c8f44b..4d6216ec9e6 100644 --- a/mozilla/js/src/jsapi.c +++ b/mozilla/js/src/jsapi.c @@ -326,6 +326,8 @@ JS_TypeOfValue(JSContext *cx, jsval v) { JSType type; JSObject *obj; + JSObjectOps *ops; + JSClass *clasp; CHECK_REQUEST(cx); if (JSVAL_IS_VOID(v)) { @@ -333,9 +335,11 @@ JS_TypeOfValue(JSContext *cx, jsval v) } else if (JSVAL_IS_OBJECT(v)) { obj = JSVAL_TO_OBJECT(v); if (obj && - (OBJ_IS_NATIVE(obj) - ? OBJ_GET_CLASS(cx, obj)->call || OBJ_GET_CLASS(cx, obj) == &js_FunctionClass - : obj->map->ops->call != 0)) { + (ops = obj->map->ops, + ops == &js_ObjectOps + ? (clasp = OBJ_GET_CLASS(cx, obj), + clasp->call || clasp == &js_FunctionClass) + : ops->call != 0)) { type = JSTYPE_FUNCTION; } else { type = JSTYPE_OBJECT; @@ -436,7 +440,7 @@ JS_BeginRequest(JSContext *cx) JS_LOCK_GC(rt); while (rt->gcLevel > 0) JS_AWAIT_GC_DONE(rt); - + /* Indicate that a request is running. */ rt->requestCount++; JS_UNLOCK_GC(rt); @@ -466,7 +470,7 @@ JS_PUBLIC_API(void) JS_YieldRequest(JSContext *cx) { JSRuntime *rt; - + CHECK_REQUEST(cx); PR_ASSERT(rt->requestCount > 0); @@ -506,7 +510,7 @@ JS_ResumeRequest(JSContext *cx) JS_LOCK_GC(rt); while (rt->gcLevel > 0) JS_AWAIT_GC_DONE(rt); - + /* Indicate that a request is running. */ rt->requestCount++; JS_UNLOCK_GC(rt); @@ -543,13 +547,13 @@ JS_DestroyContext(JSContext *cx) JS_PUBLIC_API(void*) JS_GetContextPrivate(JSContext *cx) { - return cx->pvt; + return cx->data; } JS_PUBLIC_API(void) -JS_SetContextPrivate(JSContext *cx, void *pvt) +JS_SetContextPrivate(JSContext *cx, void *data) { - cx->pvt = pvt; + cx->data = data; } JS_PUBLIC_API(JSRuntime *) @@ -597,6 +601,13 @@ JS_SetVersion(JSContext *cx, JSVersion version) return oldVersion; } +JS_PUBLIC_API(const char *) +JS_GetImplementationVersion(void) +{ + return "JavaScript-C 1.3 1998 06 30"; +} + + JS_PUBLIC_API(JSObject *) JS_GetGlobalObject(JSContext *cx) { @@ -672,8 +683,8 @@ JS_malloc(JSContext *cx, size_t nbytes) { void *p; -#ifdef XP_OS2 - if (nbytes == 0) /*DSR072897 - Windows allows this, OS/2 doesn't*/ +#if defined(XP_OS2) || defined(XP_MAC) + if (nbytes == 0) /*DSR072897 - Windows allows this, OS/2 & Mac don't*/ nbytes = 1; #endif p = malloc(nbytes); @@ -962,7 +973,7 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ if (OBJ_GET_CLASS(cx, ctor) == clasp) { /* XXXMLM - this fails in framesets that are writing over - * themselves! + * themselves! * PR_ASSERT(!OBJ_GET_PROTO(cx, ctor)); */ OBJ_SET_PROTO(cx, ctor, proto); @@ -1198,19 +1209,11 @@ JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) * so we don't need to GC-alloc constant doubles. */ jsdouble d = cds->dval; + jsint i; - /* We can't do a (jsint) cast to check against JSDOUBLE_IS_INT until we - * know that d is not NaN, or we risk a FPE on some platforms. - */ - if (JSDOUBLE_IS_NaN(d)) { - value = DOUBLE_TO_JSVAL(&cds->dval); - } else { - jsint i = (jsint)d; - - value = (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) - ? INT_TO_JSVAL(i) - : DOUBLE_TO_JSVAL(&cds->dval); - } + value = (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) + ? INT_TO_JSVAL(i) + : DOUBLE_TO_JSVAL(&cds->dval); #else ok = js_NewNumberValue(cx, cds->dval, &value); if (!ok) @@ -1754,7 +1757,7 @@ JS_Enumerate(JSContext *cx, JSObject *obj) vector[i++] = id; } } - + return ida; error: @@ -1780,9 +1783,13 @@ JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, JSAtom *atom; CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return NULL; + + atom = NULL; + if (name) { + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return NULL; + } return js_NewFunction(cx, NULL, call, nargs, flags, parent, atom); } @@ -2529,7 +2536,7 @@ JS_IsExceptionPending(JSContext *cx) { CHECK_REQUEST(cx); #if JS_HAS_EXCEPTIONS - return (JSBool) cx->fp->exceptPending; + return (JSBool) cx->fp->throwing; #else return JS_FALSE; #endif @@ -2540,7 +2547,7 @@ JS_GetPendingException(JSContext *cx, jsval *vp) { CHECK_REQUEST(cx); #if JS_HAS_EXCEPTIONS - if (!cx->fp->exceptPending) + if (!cx->fp->throwing) return JS_FALSE; *vp = cx->fp->exception; return JS_TRUE; @@ -2554,7 +2561,7 @@ JS_SetPendingException(JSContext *cx, jsval v) { CHECK_REQUEST(cx); #if JS_HAS_EXCEPTIONS - cx->fp->exceptPending = JS_TRUE; + cx->fp->throwing = JS_TRUE; cx->fp->exception = v; #endif } @@ -2564,7 +2571,7 @@ JS_ClearPendingException(JSContext *cx) { CHECK_REQUEST(cx); #if JS_HAS_EXCEPTIONS - cx->fp->exceptPending = JS_FALSE; + cx->fp->throwing = JS_FALSE; #endif } @@ -2591,7 +2598,6 @@ JS_ClearContextThread(JSContext *cx) return old; } #endif - /************************************************************************/ @@ -2649,3 +2655,4 @@ BOOL CALLBACK __loadds WEP(BOOL fSystemExit) #endif /* !_WIN32 */ #endif /* XP_OS2 */ #endif /* XP_PC */ + \ No newline at end of file diff --git a/mozilla/js/src/jsapi.h b/mozilla/js/src/jsapi.h index eec075253c2..4d012ff0bf9 100644 --- a/mozilla/js/src/jsapi.h +++ b/mozilla/js/src/jsapi.h @@ -273,7 +273,7 @@ PR_EXTERN(void*) JS_GetContextPrivate(JSContext *cx); PR_EXTERN(void) -JS_SetContextPrivate(JSContext *cx, void *pvt); +JS_SetContextPrivate(JSContext *cx, void *data); extern JS_PUBLIC_API(JSRuntime *) JS_GetRuntime(JSContext *cx); @@ -287,6 +287,9 @@ JS_GetVersion(JSContext *cx); extern JS_PUBLIC_API(JSVersion) JS_SetVersion(JSContext *cx, JSVersion version); +extern JS_PUBLIC_API(const char *) +JS_GetImplementationVersion(void); + extern JS_PUBLIC_API(JSObject *) JS_GetGlobalObject(JSContext *cx); @@ -556,7 +559,6 @@ JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, * If the object does not have a property by that name, *foundp will be * JS_FALSE and the value of *attrsp is undefined. */ - extern JS_PUBLIC_API(JSBool) JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, uintN *attrsp, JSBool *foundp); @@ -567,7 +569,6 @@ JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, * If the object does not have a property by that name, *foundp will be * JS_FALSE and nothing will be altered. */ - extern JS_PUBLIC_API(JSBool) JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, uintN attrs, JSBool *foundp); @@ -962,7 +963,7 @@ JS_ClearPendingException(JSContext *cx); #ifdef JS_THREADSAFE /* - * Associate the current thread with the given context. This is done + * Associate the current thread with the given context. This is done * implicitly by JS_NewContext. * * Returns the old thread id for this context, which should be treated as diff --git a/mozilla/js/src/jsarray.c b/mozilla/js/src/jsarray.c index 0faa7a80e74..e07937adb12 100644 --- a/mozilla/js/src/jsarray.c +++ b/mozilla/js/src/jsarray.c @@ -38,30 +38,34 @@ #include "jsobj.h" #include "jsstr.h" +/* 2^32 - 1 as a number and a string */ +#define MAXINDEX 4294967295u +#define MAXSTR "4294967295" -/* Determine if the id represents an array index. - * +/* + * Determine if the id represents an array index. + * * An id is an array index according to ECMA by (15.4): * * "Array objects give special treatment to a certain class of property names. * A property name P (in the form of a string value) is an array index if and - * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal + * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal * to 2^32-1." - * + * * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id) - * except that by using signed 32-bit integers we miss the top half of the + * except that by using signed 32-bit integers we miss the top half of the * valid range. This function checks the string representation itself; note - * that calling a standard conversion routine might allow strings such as + * that calling a standard conversion routine might allow strings such as * "08" or "4.0" as array indices, which they are not. */ static JSBool IdIsIndex(jsid id, jsuint *indexp) { - jsuint i; JSString *str; - jschar *ep; + jschar *cp; if (JSVAL_IS_INT(id)) { + jsint i; i = JSVAL_TO_INT(id); if (i < 0) return JS_FALSE; @@ -71,21 +75,31 @@ IdIsIndex(jsid id, jsuint *indexp) /* It must be a string. */ str = JSVAL_TO_STRING(id); - ep = str->chars; - if (str->length == 0 || *ep < '1' || *ep > '9') - return JS_FALSE; - i = 0; - while (*ep != 0 && JS7_ISDEC(*ep)) { - jsuint i2 = i*10 + (*ep - '0'); - if (i2 < i) - return JS_FALSE; /* overflow */ - i = i2; - ep++; + cp = str->chars; + if (JS7_ISDEC(*cp) && str->length < sizeof(MAXSTR)) { + jsuint index = JS7_UNDEC(*cp++); + jsuint oldIndex = 0; + jsint c; + if (index != 0) { + while (JS7_ISDEC(*cp)) { + oldIndex = index; + c = JS7_UNDEC(*cp); + index = 10*index + c; + cp++; + } + } + /* Make sure all characters were consumed and that it couldn't + * have overflowed. + */ + if (*cp == 0 && + (oldIndex < (MAXINDEX / 10) || + (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) + { + *indexp = index; + return JS_TRUE; + } } - if (*ep != 0 || i == 4294967295) /* 4294967295 == 2^32-1 */ - return JS_FALSE; - *indexp = i; - return JS_TRUE; + return JS_FALSE; } @@ -93,9 +107,9 @@ static JSBool ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) { jsint i; - JSBool res = JS_FALSE; - - /* It's only an array length if it's an int or a double. Some relevant + + /* + * It's only an array length if it's an int or a double. Some relevant * ECMA language is 15.4.2.2 - 'If the argument len is a number, then the * length property of the newly constructed object is set to ToUint32(len) * - I take 'is a number' to mean 'typeof len' returns 'number' in @@ -106,16 +120,18 @@ ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) /* jsuint cast does ToUint32 */ if (lengthp) *lengthp = (jsuint) i; - res = JS_TRUE; - } else if (JSVAL_IS_DOUBLE(v)) { - /* XXXmccabe I'd love to add another check here, against + return JS_TRUE; + } + if (JSVAL_IS_DOUBLE(v)) { + /* + * XXXmccabe I'd love to add another check here, against * js_DoubleToInteger(d) != d), so that ValueIsLength matches * IdIsIndex, but it doesn't seem to follow from ECMA. * (seems to be appropriate for IdIsIndex, though). */ - res = js_ValueToECMAUint32(cx, v, (uint32 *)lengthp); + return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp); } - return res; + return JS_FALSE; } diff --git a/mozilla/js/src/jsatom.c b/mozilla/js/src/jsatom.c index c0a52c22fd4..8d11ba7ba5e 100644 --- a/mozilla/js/src/jsatom.c +++ b/mozilla/js/src/jsatom.c @@ -115,8 +115,18 @@ js_compare_atom_keys(const void *k1, const void *k2) v1 = (jsval)k1, v2 = (jsval)k2; if (JSVAL_IS_STRING(v1) && JSVAL_IS_STRING(v2)) return !js_CompareStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2)); - if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) - return *JSVAL_TO_DOUBLE(v1) == *JSVAL_TO_DOUBLE(v2); + if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) { + double d1 = *JSVAL_TO_DOUBLE(v1); + double d2 = *JSVAL_TO_DOUBLE(v2); + if (JSDOUBLE_IS_NaN(d1)) + return JSDOUBLE_IS_NaN(d2); +#ifdef XP_PC + /* XXX MSVC miscompiles such that (NaN == 0) */ + if (JSDOUBLE_IS_NaN(d2)) + return JS_FALSE; +#endif + return d1 == d2; + } return v1 == v2; } diff --git a/mozilla/js/src/jscntxt.h b/mozilla/js/src/jscntxt.h index a024fedf6a0..81820cc71c5 100644 --- a/mozilla/js/src/jscntxt.h +++ b/mozilla/js/src/jscntxt.h @@ -60,10 +60,10 @@ struct JSRuntime { /* Random number generator state, used by jsmath.c. */ JSBool rngInitialized; - PRInt64 rngMultiplier; - PRInt64 rngAddend; - PRInt64 rngMask; - PRInt64 rngSeed; + int64 rngMultiplier; + int64 rngAddend; + int64 rngMask; + int64 rngSeed; jsdouble rngDscale; /* Well-known numbers held for use by this runtime's contexts. */ @@ -84,6 +84,8 @@ struct JSRuntime { void *newScriptHookData; JSDestroyScriptHook destroyScriptHook; void *destroyScriptHookData; + JSTrapHandler debuggerHandler; + void *debuggerHandlerData; /* More debugging state, see jsdbgapi.c. */ PRCList trapList; @@ -150,7 +152,7 @@ struct JSContext { JSErrorReporter errorReporter; /* Client opaque pointer */ - void *pvt; + void *data; /* Java environment and JS errors to throw as exceptions. */ void *javaEnv; @@ -161,6 +163,7 @@ struct JSContext { JSPackedBool gcActive; jsrefcount requestDepth; #endif + JSStackFrame *dormantFrameChain; /* dormant frame chains */ }; typedef struct JSInterpreterHooks { diff --git a/mozilla/js/src/jsconfig.h b/mozilla/js/src/jsconfig.h index 6f55833ebf2..fb20b6ad429 100644 --- a/mozilla/js/src/jsconfig.h +++ b/mozilla/js/src/jsconfig.h @@ -20,7 +20,7 @@ * JS configuration macros. */ #ifndef JS_VERSION -#define JS_VERSION 130 +#define JS_VERSION 140 #endif #if JS_VERSION == 100 @@ -67,6 +67,7 @@ #define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ #define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ #define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ #elif JS_VERSION == 110 @@ -112,6 +113,7 @@ #define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ #define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ #define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ #elif JS_VERSION == 120 @@ -157,6 +159,7 @@ #define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ #define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ #define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ #elif JS_VERSION == 130 @@ -202,6 +205,53 @@ #define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ #define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ #define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ + +#elif JS_VERSION == 140 + +#define JS_BUG_AUTO_INDEX_PROPS 0 /* new object o: o.p = v sets o[0] */ +#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_SHORT_CIRCUIT 0 /* 1 && 1 => true, 1 && 0 => 0 bug */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 1 /* with(o)function f(){} sets o.f */ +#define JS_BUG_SET_ENUMERATE 0 /* o.p=q flags o.p JSPROP_ENUMERATE */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, str.substr, etc */ +#define JS_HAS_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 1 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 1 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 1 /* has string.replace(re, lambda) */ +#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 1 /* has XDR API and object methods */ +#define JS_HAS_EXCEPTIONS 1 /* has exception handling */ +#define JS_HAS_UNDEFINED 1 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 1 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 1 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ #else diff --git a/mozilla/js/src/jsdate.c b/mozilla/js/src/jsdate.c index 957ad577140..e22ec7a45fb 100644 --- a/mozilla/js/src/jsdate.c +++ b/mozilla/js/src/jsdate.c @@ -305,9 +305,9 @@ static jsdouble LocalTZA; static jsdouble DaylightSavingTA(jsdouble t) { - PRInt64 PR_t; - PRInt64 ms2us; - PRInt64 offset; + int64 PR_t; + int64 ms2us; + int64 offset; jsdouble result; /* abort if NaN */ @@ -1694,7 +1694,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) /* Date called as function */ if (!cx->fp->constructing) { - PRInt64 us, ms, us2ms; + int64 us, ms, us2ms; jsdouble time; /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS', @@ -1710,7 +1710,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) /* Date called as constructor */ if (argc == 0) { - PRInt64 us, ms, us2ms; + int64 us, ms, us2ms; jsdouble time; date = date_constructor(cx, obj); @@ -2020,3 +2020,4 @@ js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds) msFromTime(local)); *date = UTC(local); } + \ No newline at end of file diff --git a/mozilla/js/src/jsdbgapi.c b/mozilla/js/src/jsdbgapi.c index 3c67f34cdc1..314f2e7d0bd 100644 --- a/mozilla/js/src/jsdbgapi.c +++ b/mozilla/js/src/jsdbgapi.c @@ -757,3 +757,13 @@ JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda) } JS_free(cx, pd); } + +/************************************************************************/ + +JS_PUBLIC_API(JSBool) +JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure) +{ + rt->debuggerHandler = handler; + rt->debuggerHandlerData = closure; + return JS_TRUE; +} diff --git a/mozilla/js/src/jsdbgapi.h b/mozilla/js/src/jsdbgapi.h index 66b2f09d6b0..000b734879c 100644 --- a/mozilla/js/src/jsdbgapi.h +++ b/mozilla/js/src/jsdbgapi.h @@ -211,6 +211,11 @@ JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda); extern JS_PUBLIC_API(void) JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda); +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure); + PR_END_EXTERN_C #endif /* jsdbgapi_h___ */ diff --git a/mozilla/js/src/jsemit.c b/mozilla/js/src/jsemit.c index 84fd65e9f89..7d5d5781644 100644 --- a/mozilla/js/src/jsemit.c +++ b/mozilla/js/src/jsemit.c @@ -235,10 +235,18 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, { JSStmtInfo *stmt; intN index; + uint16 finallyIndex = 0; ptrdiff_t offset, delta; for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { switch (stmt->type) { + case STMT_FINALLY: + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit3(cx, cg, JSOP_JSR, JUMP_OFFSET_HI(finallyIndex), + JUMP_OFFSET_LO(finallyIndex)) < 0) + return -1; + finallyIndex--; + break; case STMT_WITH: if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return -1; @@ -401,7 +409,6 @@ EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) JSAtom *atom; JSAtomListElement *ale; - ival = (jsint)dval; if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) { if (ival == 0) return js_Emit1(cx, cg, JSOP_ZERO) >= 0; @@ -439,6 +446,57 @@ js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, return JS_TRUE; } +#if JS_HAS_EXCEPTIONS +#define BYTECODE_ITER(pc, max, body) \ + while (pc < max) { \ + JSCodeSpec *cs = &js_CodeSpec[(JSOp)*pc]; \ + body; \ + if ((cs->format & JOF_TYPEMASK) == JOF_TABLESWITCH || \ + (cs->format & JOF_TYPEMASK) == JOF_LOOKUPSWITCH) { \ + pc += GET_JUMP_OFFSET(pc); \ + } else { \ + pc += cs->length; \ + } \ + } + +static JSBool +FixupFinallyJumps(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t tryStart, + ptrdiff_t finallyIndex) +{ + jsbytecode *pc; + pc = cg->base + tryStart; + BYTECODE_ITER(pc, cg->next, \ + if (*pc == JSOP_JSR) { \ + ptrdiff_t index = GET_JUMP_OFFSET(pc); \ + if (index <= 0) { \ + if (index == 0) { \ + index = finallyIndex - (pc - cg->base); \ + } else { \ + index++; \ + } \ + CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, index); \ + } \ + } \ + ); + return JS_TRUE; +} + +static JSBool +FixupCatchJumps(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t tryStart, + ptrdiff_t postCatch) +{ + jsbytecode *pc; + pc = cg->base + tryStart; + BYTECODE_ITER(pc, cg->next, \ + if (*pc == JSOP_GOTO && !GET_JUMP_OFFSET(pc)) { \ + CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, postCatch - (pc - cg->base)); \ + } \ + ); + return JS_TRUE; +} + +#endif + JSBool js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) { @@ -462,8 +520,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) delta = lineno - cg->currentLine; cg->currentLine = lineno; if (delta) { - /* If delta requires too many SRC_NEWLINE notes, use SRC_SETLINE. */ - if (delta >= (uintN)(2 + (lineno > SN_2BYTE_OFFSET_MASK))) { + /* + * Encode any change in the current source line number by using either + * several SRC_NEWLINE notes or one SRC_SETLINE note, whichever + * consumes less space. + */ + if (delta >= (uintN)(2 + ((lineno > SN_3BYTE_OFFSET_MASK) << 1))) { if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)lineno) < 0) return JS_FALSE; } else { @@ -479,20 +541,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) { JSFunction *fun; + /* Fold constants and generate code for the function's body. */ + pn2 = pn->pn_body; + if (!js_FoldConstants(cx, pn2)) + return JS_FALSE; if (!js_InitCodeGenerator(cx, &cg2, cg->filename, pn->pn_pos.begin.lineno, cg->principals)) { return JS_FALSE; } - ok = js_FoldConstants(cx, pn->pn_body); - if (ok) { - cg2.treeContext.tryCount = pn->pn_tryCount; - fun = pn->pn_fun; - ok = js_EmitFunctionBody(cx, &cg2, pn->pn_body, fun); - } - js_ResetCodeGenerator(cx, &cg2); - if (!ok) + cg2.treeContext.tryCount = pn->pn_tryCount; + fun = pn->pn_fun; + if (!js_EmitFunctionBody(cx, &cg2, pn2, fun)) return JS_FALSE; + js_ResetCodeGenerator(cx, &cg2); /* Make the function object a literal in the outer script's pool. */ atom = js_AtomizeObject(cx, fun->object, 0); @@ -1070,23 +1132,46 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) #if JS_HAS_EXCEPTIONS case TOK_TRY: { - ptrdiff_t start, end, catch; -#if shaver_finally - /* if this try has a finally, push marker onto treeContext */ + ptrdiff_t start, end, catchStart, finallyCatch, catchjmp = -1; + JSParseNode *iter = pn; + + /* XXX use -(CG_OFFSET + 1) */ +#define EMIT_FINALLY_JSR(cx, cg) \ + PR_BEGIN_MACRO \ + if (!js_Emit3(cx, cg, JSOP_JSR, 0, 0)) \ + return JS_FALSE; \ + PR_END_MACRO +; + /* + * When a finally block is `active' (STMT_FINALLY on the treeContext), + * non-local jumps result in a JSR being written into the bytecode + * stream for later fixup. The jsr is written with offset 0 for the + * innermost finally, -1 for the next, etc. As the finally fixup code + * runs for each finished try/finally, it will fix the JSRs with + * offset 0 to match the appropriate finally code for its block + * and decrement all others by one. + * + * NOTE: This will cause problems if we use JSRs for something other + * than finally handling in the future. Caveat hacker! + */ + if (pn->pn_kid3) - js_PushStatement(&cg->treeContext, &stmtInto, STMT_FINALLY, + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FINALLY, CG_OFFSET(cg)); -#endif /* mark try location for decompilation, then emit try block */ - if (js_NewSrcNote(cx, cg, SRC_TRY) < 0 || + if (js_NewSrcNote2(cx, cg, SRC_TRYFIN, 0) < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) return JS_FALSE; start = CG_OFFSET(cg); if(!js_EmitTree(cx, cg, pn->pn_kid1)) return JS_FALSE; - - /* emit (hidden) jump over try and/or finally */ + + /* emit (hidden) jump over catch and/or finally */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + if (pn->pn_kid3) + EMIT_FINALLY_JSR(cx, cg); if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); @@ -1094,66 +1179,198 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; end = CG_OFFSET(cg); - /* if this try has a catch block, emit it */ if (pn->pn_kid2) { - catch = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn->pn_kid2)) + catchStart = end; + /* + * The emitted code for a catch block looks like: + * + * [ popscope ] only if 2nd+ catch block + * name Object + * pushobj + * newinit + * exception + * initprop maybe marked SRC_CATCHGUARD + * enterwith + * [< catchguard code >] if there's a catchguard + * ifeq + * < catch block contents > + * leavewith + * goto non-local; finally applies + * + * If there's no catch block without a catchguard, the last + * points to rethrow code. This + * code will jsr to the finally code if appropriate, and is + * also used for the catch-all trynote for capturing exceptions + * thrown from catch{} blocks. + */ + do { + JSStmtInfo stmtInfo2; + JSParseNode *disc; + ptrdiff_t guardnote; + + iter = iter->pn_kid2; + disc = iter->pn_kid1; + + if (catchjmp != -1) { + /* fix up and clean up previous catch block */ + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp); + if ((uintN)++cg->stackDepth > cg->maxStackDepth) + cg->maxStackDepth = cg->stackDepth; + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) + return JS_FALSE; + } + + /* non-zero guardnote is length of catchguard */ + guardnote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0); + if (guardnote < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) + return JS_FALSE; + + /* construct the scope holder and push it on */ + ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_NAME, ale->index); + + if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0 || + js_Emit1(cx, cg, JSOP_NEWINIT) < 0 || + js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) + return JS_FALSE; + + /* setprop */ + ale = js_IndexAtom(cx, disc->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + + EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ale->index); + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) + return JS_FALSE; + + /* boolean_expr */ + if (disc->pn_expr) { + ptrdiff_t guardstart = CG_OFFSET(cg); + if (!js_EmitTree(cx, cg, disc->pn_expr)) + return JS_FALSE; + /* ifeq */ + catchjmp = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); + if (catchjmp < 0) + return JS_FALSE; + if (!js_SetSrcNoteOffset(cx, cg, guardnote, 0, + (ptrdiff_t)CG_OFFSET(cg) - + guardstart)) + return JS_FALSE; + } + /* emit catch block */ + js_PushStatement(&cg->treeContext, &stmtInfo2, STMT_WITH, + CG_OFFSET(cg)); + if (!js_EmitTree(cx, cg, iter->pn_kid3)) + return JS_FALSE; + js_PopStatementCG(cx, cg); + + /* + * jump over the remaining catch blocks + * this counts as a non-local jump, so do the finally thing + */ + + /* popscope */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) + return JS_FALSE; + + /* jsr , if required */ + if (pn->pn_kid3) + EMIT_FINALLY_JSR(cx, cg); + + /* this will get fixed up to jump to after catch/finally */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit3(cx, cg, JSOP_GOTO, 0, 0) < 0) + return JS_FALSE; + if (!iter->pn_kid2) + break; + } while (iter); + + } + + /* + * we use a [leavewith],[jsr],rethrow block for rethrowing + * when there's no unguarded catch, and also for + * running finally code while letting an uncaught exception + * pass through + */ + if (pn->pn_kid3 || + (catchjmp != -1 && iter->pn_kid1->pn_expr)) { + if (catchjmp != -1) { + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp); + } + /* last discriminant jumps to rethrow if none match */ + if ((uintN)++cg->stackDepth > cg->maxStackDepth) + cg->maxStackDepth = cg->stackDepth; + if (pn->pn_kid2 && + (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)) + return JS_FALSE; + + if (pn->pn_kid3) { + finallyCatch = CG_OFFSET(cg); + EMIT_FINALLY_JSR(cx, cg); + } + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_EXCEPTION) < 0 || + js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_THROW) < 0) return JS_FALSE; } -#if shaver_finally /* if we've got a finally, it goes here, and we have to fix up the jsrs that might have been emitted before non-local jumps */ + if (pn->pn_kid3) { - ptrdiff_t finallyIndex = CG_OFFSET(CG); - js_PopStatement(tc); - if (!FixupFinallyJumps(cx, cg, tryStart, finallyIndex)) + ptrdiff_t finallyIndex; + finallyIndex = CG_OFFSET(cg); + if (!FixupFinallyJumps(cx, cg, start, finallyIndex)) return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_kid3)) + js_PopStatementCG(cx, cg); + if (js_NewSrcNote2(cx, cg, SRC_TRYFIN, 1) < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0 || + !js_EmitTree(cx, cg, pn->pn_kid3) || + js_Emit1(cx, cg, JSOP_RETSUB) < 0) return JS_FALSE; + } -#endif - /* come from post-try */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) return JS_FALSE; + /* fix up the end-of-try/catch jumps to come here */ + if (!FixupCatchJumps(cx, cg, start, CG_OFFSET(cg))) + return JS_FALSE; + + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + /* * Add the try note last, to let post-order give us the right ordering * (first to last, inner to outer). */ - if (!js_NewTryNote(cx, cg, start, end, catch, 0)) + if (pn->pn_kid2 && + !js_NewTryNote(cx, cg, start, end, catchStart)) + return JS_FALSE; + + /* + * If we've got a finally, mark try+catch region with additional + * trynote to catch exceptions (re)thrown from a catch block or + * for the try{}finally{} case. + */ + if (pn->pn_kid3 && + !js_NewTryNote(cx, cg, start, finallyCatch-1, finallyCatch)) return JS_FALSE; break; } - case TOK_CATCH: - /* - * Catch blocks are very magical. Mainly, the magic involves - * special generation for the declaration/conditional, and - * mucking with the stack depth counter so that the implicit - * push of the exception that happens at runtime doesn't make - * the code generator very confused. - * - * If a catch block has a finally associated with it, the try - * block will already have pushed the appropriate statement info - * onto the tree context, so we don't need to worry about it. - */ - noteIndex = js_NewSrcNote(cx, cg, SRC_CATCH); - if (noteIndex < 0 || - !js_Emit1(cx, cg, JSOP_NOP)) - return JS_FALSE; - - /* need to adjust stack depth */ - if ((uintN)++cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; - if (!js_EmitTree(cx, cg, pn->pn_left) || - !js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - break; #endif /* JS_HAS_EXCEPTIONS */ case TOK_VAR: @@ -1788,6 +2005,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_PRIMARY: return js_Emit1(cx, cg, pn->pn_op) >= 0; +#if JS_HAS_DEBUGGER_KEYWORD + case TOK_DEBUGGER: + return js_Emit1(cx, cg, JSOP_DEBUGGER) >= 0; +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + default: PR_ASSERT(0); } @@ -1816,7 +2038,7 @@ JS_FRIEND_DATA(const char *) js_SrcNoteName[] = { "cont2label", "switch", "funcdef", - "try", + "tryfin", "catch", "newline", "setline", @@ -1844,8 +2066,8 @@ uint8 js_SrcNoteArity[] = { 1, /* SRC_CONT2LABEL */ 1, /* SRC_SWITCH */ 1, /* SRC_FUNCDEF */ - 0, /* SRC_TRY */ - 0, /* SRC_CATCH */ + 1, /* SRC_TRYFIN */ + 1, /* SRC_CATCHGUARD */ 0, /* SRC_NEWLINE */ 1, /* SRC_SETLINE */ 0 /* SRC_XDELTA */ @@ -1982,8 +2204,8 @@ js_SrcNoteLength(jssrcnote *sn) if (!arity) return 1; for (base = sn++; --arity >= 0; sn++) { - if (*sn & SN_2BYTE_OFFSET_FLAG) - sn++; + if (*sn & SN_3BYTE_OFFSET_FLAG) + sn +=2 ; } return sn - base; } @@ -1995,11 +2217,11 @@ js_GetSrcNoteOffset(jssrcnote *sn, uintN which) PR_ASSERT(SN_TYPE(sn) != SRC_XDELTA); PR_ASSERT(which < js_SrcNoteArity[SN_TYPE(sn)]); for (sn++; which; sn++, which--) { - if (*sn & SN_2BYTE_OFFSET_FLAG) - sn++; + if (*sn & SN_3BYTE_OFFSET_FLAG) + sn += 2; } - if (*sn & SN_2BYTE_OFFSET_FLAG) - return (ptrdiff_t)((*sn & SN_2BYTE_OFFSET_MASK) << 8) | sn[1]; + if (*sn & SN_3BYTE_OFFSET_FLAG) + return (ptrdiff_t)((((uint32)(*sn & SN_3BYTE_OFFSET_MASK)) << 16) | (sn[1] << 8) | sn[2]); return (ptrdiff_t)*sn; } @@ -2010,7 +2232,7 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, jssrcnote *sn; ptrdiff_t diff; - if ((size_t)offset >= (size_t)(SN_2BYTE_OFFSET_FLAG << 8)) { + if (offset >= (((ptrdiff_t)SN_3BYTE_OFFSET_FLAG) << 16)) { ReportStatementTooLarge(cx, cg); return JS_FALSE; } @@ -2020,26 +2242,35 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, PR_ASSERT(SN_TYPE(sn) != SRC_XDELTA); PR_ASSERT(which < js_SrcNoteArity[SN_TYPE(sn)]); for (sn++; which; sn++, which--) { - if (*sn & SN_2BYTE_OFFSET_FLAG) - sn++; + if (*sn & SN_3BYTE_OFFSET_FLAG) + sn += 2; } - /* See if the new offset requires two bytes. */ - if ((uintN)offset > (uintN)SN_2BYTE_OFFSET_MASK) { - /* Maybe this offset was already set to a two-byte value. */ - if (!(*sn & SN_2BYTE_OFFSET_FLAG)) { - /* Losing, need to insert another byte for this offset. */ + /* See if the new offset requires three bytes. */ + if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) { + /* Maybe this offset was already set to a three-byte value. */ + if (!(*sn & SN_3BYTE_OFFSET_FLAG)) { + /* Losing, need to insert another two bytes for this offset. */ index = sn - cg->notes; - if (cg->noteCount++ % SNINCR == 0) { + cg->noteCount += 2; + + /* + * Simultaneously test to see if the source note array must grow to + * accomodate either the first or second byte of additional storage + * required by this 3-byte offset. + */ + if ((cg->noteCount - 1) % SNINCR <= 1) { if (!GrowSrcNotes(cx, cg)) return JS_FALSE; sn = cg->notes + index; - } - diff = cg->noteCount - (index + 2); + } + diff = cg->noteCount - (index + 3); + PR_ASSERT(diff >= 0); if (diff > 0) - memmove(sn + 2, sn + 1, diff * sizeof(jssrcnote)); + memmove(sn + 3, sn + 1, diff * sizeof(jssrcnote)); } - *sn++ = (jssrcnote)(SN_2BYTE_OFFSET_FLAG | (offset >> 8)); + *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16)); + *sn++ = (jssrcnote)(offset >> 8); } *sn = (jssrcnote)offset; return JS_TRUE; @@ -2078,18 +2309,16 @@ js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg) JS_FRIEND_API(JSTryNote *) js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, - ptrdiff_t end, ptrdiff_t catch, ptrdiff_t finally) + ptrdiff_t end, ptrdiff_t catchStart) { - JSTryNote *cur; + JSTryNote *tn; PR_ASSERT(cg->tryNext < cg->tryLimit); - cur = cg->tryNext; - cg->tryNext++; - cur->start = start; - cur->end = end; - cur->catch = catch; - cur->finally = finally; - return cur; + tn = cg->tryNext++; + tn->start = start; + tn->length = end - start; + tn->catchStart = catchStart; + return tn; } JS_FRIEND_API(JSBool) @@ -2112,9 +2341,8 @@ js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote **tryp) } memcpy(final, tmp, count * sizeof(JSTryNote)); final[count].start = 0; - final[count].end = 0; - final[count].catch = 0; - final[count].finally = 0; + final[count].length = CG_OFFSET(cg); + final[count].catchStart = 0; *tryp = final; return JS_TRUE; } diff --git a/mozilla/js/src/jsemit.h b/mozilla/js/src/jsemit.h index 12d08ced3a7..f66af45596e 100644 --- a/mozilla/js/src/jsemit.h +++ b/mozilla/js/src/jsemit.h @@ -73,6 +73,7 @@ struct JSTreeContext { /* tree context for semantic checks */ #define TCF_IN_FUNCTION 0x01 /* parsing inside function body */ #define TCF_RETURN_EXPR 0x02 /* function has 'return expr;' */ #define TCF_RETURN_VOID 0x04 /* function has 'return;' */ +#define TCF_IN_FOR_INIT 0x08 /* parsing init expr of for; exclude 'in' */ #define INIT_TREE_CONTEXT(tc) \ ((tc)->flags = 0, (tc)->tryCount = 0, (tc)->topStmt = NULL) @@ -247,7 +248,7 @@ typedef enum JSSrcNoteType { SRC_ASSIGNOP = 8, /* += or another assign-op follows */ SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ SRC_PAREN = 10, /* JSOP_NOP generated to mark user parens */ - SRC_HIDDEN = 11, /* JSOP_LEAVEWITH for break/continue in with */ + SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */ SRC_PCBASE = 12, /* offset of first obj.prop.subprop bytecode */ SRC_LABEL = 13, /* JSOP_NOP for label: with atomid immediate */ SRC_LABELBRACE = 14, /* JSOP_NOP for label: {...} begin brace */ @@ -256,8 +257,8 @@ typedef enum JSSrcNoteType { SRC_CONT2LABEL = 17, /* JSOP_GOTO for 'continue label' with atomid */ SRC_SWITCH = 18, /* JSOP_*SWITCH with offset to end of switch */ SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */ - SRC_TRY = 20, /* JSOP_NOP for beginning of try{} section */ - SRC_CATCH = 21, /* beginning of catch block (at conditional) */ + SRC_TRYFIN = 20, /* JSOP_NOP for try{} or finally{} section */ + SRC_CATCH = 21, /* catch block has guard */ SRC_NEWLINE = 22, /* bytecode follows a source newline */ SRC_SETLINE = 23, /* a file-absolute source line number note */ SRC_XDELTA = 24 /* 24-31 are for extended delta notes */ @@ -295,11 +296,11 @@ typedef enum JSSrcNoteType { /* * Offset fields follow certain notes and are frequency-encoded: an offset in - * [0,127] consumes one byte, an offset in [128,32767] takes two, and the high - * bit of the first byte is set. + * [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and + * the high bit of the first byte is set. */ -#define SN_2BYTE_OFFSET_FLAG 0x80 -#define SN_2BYTE_OFFSET_MASK 0x7f +#define SN_3BYTE_OFFSET_FLAG 0x80 +#define SN_3BYTE_OFFSET_MASK 0x7f extern JS_FRIEND_DATA(const char *) js_SrcNoteName[]; extern JS_FRIEND_DATA(uint8) js_SrcNoteArity[]; @@ -361,7 +362,7 @@ js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg); */ extern JS_FRIEND_API(JSTryNote *) js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, - ptrdiff_t end, ptrdiff_t catch, ptrdiff_t finally); + ptrdiff_t end, ptrdiff_t catchStart); /* * Finish generating exception information, and copy it to JS_malloc diff --git a/mozilla/js/src/jsfun.c b/mozilla/js/src/jsfun.c index b7a8489e491..b710e334c1b 100644 --- a/mozilla/js/src/jsfun.c +++ b/mozilla/js/src/jsfun.c @@ -282,10 +282,14 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent, /* Splice callobj into the scope chain. */ if (!withobj) { - for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { + for (obj = fp->scopeChain; obj; obj = parent) { if (OBJ_GET_CLASS(cx, obj) != &js_WithClass) break; - withobj = obj; + parent = OBJ_GET_PARENT(cx, obj); + if (parent == funobj) { + withobj = obj; + break; + } } } if (withobj) @@ -432,8 +436,8 @@ call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_TRUE; } -static JSBool -call_getVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +JSBool +js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; @@ -447,8 +451,8 @@ call_getVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_TRUE; } -static JSBool -call_setVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +JSBool +js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; @@ -542,8 +546,8 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, } else if (getter == js_GetLocalVariable) { vp = fp->vars; nslots = fp->nvars; - getter = call_getVariable; - setter = call_setVariable; + getter = js_GetCallVariable; + setter = js_SetCallVariable; } if (vp) { slot = (uintN)JSVAL_TO_INT(propid); @@ -765,7 +769,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) case ARGS_LENGTH: if (!JSVERSION_IS_ECMA(cx->version)) - *vp = INT_TO_JSVAL((jsint)(fp->fun ? fp->argc : fun->nargs)); + *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs)); else case FUN_ARITY: *vp = INT_TO_JSVAL((jsint)fun->nargs); @@ -809,6 +813,39 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_TRUE; } + +static JSBool +fun_enumProperty(JSContext *cx, JSObject *obj) +{ + JSScope *scope; + JSScopeProperty *sprop; + + /* Because properties of function objects such as "length" are + * not defined in function_props to avoid interfering with + * unqualified lookups in local scopes (which pass through the + * function object as a stand-in for the call object), we + * must twiddle any of the special properties not to be enumer- + * ated in this callback, rather than simply predefining the + * properties without JSPROP_ENUMERATE. + */ + + JS_LOCK_OBJ(cx, obj); + scope = (JSScope *) obj->map; + for (sprop = scope->props; sprop; sprop = sprop->next) { + jsval id = sprop->id; + if (!JSVAL_IS_INT(id)) { + if (id == ATOM_KEY(cx->runtime->atomState.arityAtom) || + id == ATOM_KEY(cx->runtime->atomState.lengthAtom) || + id == ATOM_KEY(cx->runtime->atomState.callerAtom) || + id == ATOM_KEY(cx->runtime->atomState.nameAtom)) + { + sprop->attrs &= ~JSPROP_ENUMERATE; + } + } + } + return JS_TRUE; +} + static JSBool fun_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { @@ -1002,10 +1039,10 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) } if (!JS_XDRStringOrNull(xdr, &atomstr) || - !JS_XDRUint8(xdr, &fun->nargs) || - !JS_XDRUint8(xdr, &fun->flags) || + !JS_XDRUint16(xdr, &fun->nargs) || !JS_XDRUint16(xdr, &fun->extra) || - !JS_XDRUint16(xdr, &fun->nvars)) + !JS_XDRUint16(xdr, &fun->nvars) || + !JS_XDRUint8(xdr, &fun->flags)) return JS_FALSE; /* do arguments and local vars */ @@ -1121,7 +1158,7 @@ JSClass js_FunctionClass = { JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, fun_delProperty, fun_getProperty, fun_setProperty, - JS_EnumerateStub, (JSResolveOp)fun_resolve, + fun_enumProperty, (JSResolveOp)fun_resolve, fun_convert, fun_finalize, NULL, NULL, NULL, NULL, @@ -1400,7 +1437,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JS_free(cx, collected_args); return JS_FALSE; } - + tt = js_GetToken(cx, ts); /* The argument string may be empty or contain no tokens. */ if (tt != TOK_EOF) { @@ -1456,7 +1493,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) PR_ASSERT(sprop); sprop->id = INT_TO_JSVAL(fun->nargs++); OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop); - + /* Done with the NAME; get the next token. */ tt = js_GetToken(cx, ts); /* Stop if we've reached the end of the string. */ @@ -1469,7 +1506,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (tt == TOK_COMMA) tt = js_GetToken(cx, ts); } - } + } /* Clean up. */ JS_free(cx, collected_args); if (!js_CloseTokenStream(cx, ts)) diff --git a/mozilla/js/src/jsfun.h b/mozilla/js/src/jsfun.h index 480a31347ed..0d9060ea37f 100644 --- a/mozilla/js/src/jsfun.h +++ b/mozilla/js/src/jsfun.h @@ -30,11 +30,11 @@ struct JSFunction { jsrefcount nrefs; /* number of referencing objects */ JSObject *object; /* back-pointer to GC'ed object header */ JSNative call; /* native method pointer or null */ - uint8 nargs; /* minimum number of actual arguments */ - uint8 flags; /* bound method and other flags, see jsapi.h */ + uint16 nargs; /* minimum number of actual arguments */ uint16 extra; /* number of arg slots for local GC roots */ uint16 nvars; /* number of local variables */ - uint16 spare; /* reserved for future use */ + uint8 flags; /* bound method and other flags, see jsapi.h */ + uint8 spare; /* reserved for future use */ JSAtom *atom; /* name for diagnostics and decompiling */ JSScript *script; /* interpreted bytecode descriptor or null */ }; @@ -85,6 +85,12 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent, extern JSBool js_PutCallObject(JSContext *cx, JSStackFrame *fp); +extern JSBool +js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + extern JSObject * js_GetArgsObject(JSContext *cx, JSStackFrame *fp); diff --git a/mozilla/js/src/jsgc.c b/mozilla/js/src/jsgc.c index 4bb2d97325b..4f402bc6091 100644 --- a/mozilla/js/src/jsgc.c +++ b/mozilla/js/src/jsgc.c @@ -615,7 +615,7 @@ js_GC(JSContext *cx) PRArena *a, *ma, *fa, **ap, **fap; jsval v, *vp, *sp; pruword begin, end; - JSStackFrame *fp; + JSStackFrame *fp, *chain; void *mark; uint8 flags, *flagp; JSGCThing *thing, *final, **flp, **oflp; @@ -703,8 +703,19 @@ restart: js_MarkAtomState(&rt->atomState, gc_mark); iter = NULL; while ((acx = js_ContextIterator(rt, &iter)) != NULL) { - fp = acx->fp; - if (fp) { + /* Iterate frame chain and dormant chains. Temporarily tack current + * frame onto the head of the dormant list to ease iteration. + * + * (NOTE: see comment on this whole 'dormant' thing in js_Execute) + */ + chain = acx->fp; + if (chain) { + PR_ASSERT(!chain->dormantNext); + chain->dormantNext = acx->dormantFrameChain; + } else { + chain = acx->dormantFrameChain; + } + for (fp=chain; fp; fp = chain = chain->dormantNext) { sp = fp->sp; if (sp) { for (a = acx->stackPool.first.next; a; a = a->next) { @@ -736,6 +747,9 @@ restart: GC_MARK(rt, fp->sharpArray, "sharp array", NULL); } while ((fp = fp->down) != NULL); } + /* cleanup temporary link */ + if (acx->fp) + acx->fp->dormantNext = NULL; GC_MARK(rt, acx->globalObject, "global object", NULL); GC_MARK(rt, acx->newborn[GCX_OBJECT], "newborn object", NULL); GC_MARK(rt, acx->newborn[GCX_STRING], "newborn string", NULL); diff --git a/mozilla/js/src/jsinterp.c b/mozilla/js/src/jsinterp.c index d83be9f0038..1020312eeb2 100644 --- a/mozilla/js/src/jsinterp.c +++ b/mozilla/js/src/jsinterp.c @@ -24,6 +24,11 @@ #include #include #include "prtypes.h" +#ifndef NSPR20 +#include "prarena.h" +#else +#include "plarena.h" +#endif #include "prlog.h" #include "jsapi.h" #include "jsarray.h" @@ -139,7 +144,6 @@ static JSClass prop_iterator_class = { jsint _i; \ jsval _v; \ \ - _i = (jsint)d; \ if (JSDOUBLE_IS_INT(d, _i) && INT_FITS_IN_JSVAL(_i)) { \ _v = INT_TO_JSVAL(_i); \ } else { \ @@ -390,6 +394,7 @@ js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) } + /* * Find a function reference and its 'this' object implicit first parameter * under argc arguments on cx's stack, and call the function. Push missing @@ -417,9 +422,15 @@ js_Invoke(JSContext *cx, uintN argc, JSBool constructing) fp = cx->fp; sp = fp->sp; - /* Once vp is set, control must flow through label out2: to return. */ + /* + * Set vp to the callable value's stack slot (it's where rval goes). + * Once vp is set, control must flow through label out2: to return. + * Set frame.rval early so native class and object ops can throw and + * return false, causing a goto out2 with ok set to false. + */ vp = sp - (2 + argc); v = *vp; + frame.rval = JSVAL_VOID; /* A callable must be an object reference. */ if (JSVAL_IS_PRIMITIVE(v)) @@ -440,11 +451,12 @@ js_Invoke(JSContext *cx, uintN argc, JSBool constructing) if (!ok) goto out2; if (!JSVAL_IS_FUNCTION(cx, v)) { - fun = NULL; + fun = NULL; script = NULL; minargs = nvars = 0; } else { funobj = JSVAL_TO_OBJECT(v); + parent = OBJ_GET_PARENT(cx, funobj); fun = JS_GetPrivate(cx, funobj); if (clasp != &js_ClosureClass) { @@ -478,16 +490,15 @@ have_fun: thisp = parent; else parent = NULL; - } + } } - /* Initialize a stack frame, except for thisp and scopeChain. */ + /* Initialize most of frame, except for thisp and scopeChain. */ frame.callobj = frame.argsobj = NULL; frame.script = script; frame.fun = fun; frame.argc = argc; frame.argv = sp - argc; - frame.rval = constructing ? OBJECT_TO_JSVAL(thisp) : JSVAL_VOID; frame.nvars = nvars; frame.vars = sp; frame.down = fp; @@ -499,16 +510,21 @@ have_fun: frame.constructing = constructing; frame.overrides = 0; frame.debugging = JS_FALSE; - frame.exceptPending = JS_FALSE; + frame.throwing = JS_FALSE; + frame.dormantNext = NULL; /* Compute the 'this' parameter and store it in frame. */ - if (thisp) { - /* Some objects (e.g., With) delegate this to another object. */ + if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) { + /* Some objects (e.g., With) delegate 'this' to another object. */ thisp = OBJ_THIS_OBJECT(cx, thisp); if (!thisp) { ok = JS_FALSE; goto out2; } + + /* Default return value for a constructor is the new object. */ + if (constructing) + frame.rval = OBJECT_TO_JSVAL(thisp); } else { /* * ECMA requires "the global object", but in the presence of multiple @@ -526,7 +542,16 @@ have_fun: * * The alert should display "true". */ - thisp = parent ? parent : cx->globalObject; + PR_ASSERT(!constructing); + if (parent == NULL) { + parent = cx->globalObject; + } else { + /* walk up to find the top-level object */ + JSObject *tmp; + thisp = parent; + while ((tmp = OBJ_GET_PARENT(cx, thisp)) != NULL) + thisp = tmp; + } } frame.thisp = thisp; @@ -631,8 +656,8 @@ have_fun: #if JS_HAS_EXCEPTIONS /* Native or script returns JS_FALSE on error or uncaught exception */ - if (!ok && frame.exceptPending) { - fp->exceptPending = JS_TRUE; + if (!ok && frame.throwing) { + fp->throwing = JS_TRUE; fp->exception = frame.exception; } #endif @@ -660,13 +685,13 @@ out: ok &= js_PutArgsObject(cx, &frame); #endif - /* Pop everything off the stack and store the return value. */ + /* Pop everything off the stack and restore cx->fp. */ PR_ARENA_RELEASE(&cx->stackPool, mark); cx->fp = fp; - *vp = frame.rval; out2: - /* Must save sp so throw code can store an exception at sp[-1]. */ + /* Store the return value and restore sp just above it. */ + *vp = frame.rval; fp->sp = vp + 1; return ok; @@ -754,10 +779,38 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSFunction *fun, frame.constructing = JS_FALSE; frame.overrides = 0; frame.debugging = debugging; - frame.exceptPending = JS_FALSE; + frame.throwing = JS_FALSE; + frame.dormantNext = NULL; + + + /* Here we wrap the call to js_Interpret with code to (conditionally) + * save and restore the old stack frame chain into a chain of 'dormant' + * frame chains. Since we are replacing cx->fp we were running into the + * problem that if gc was called, then some of the objects associated + * with the old frame chain (stored here in the C stack as 'oldfp') were + * not rooted and were being collected. This was bad. So, now we + * preserve the links to these 'dormant' frame chains in cx before + * calling js_Interpret and cleanup afterwards. gc walks these dormant + * chains and marks objects in the same way that it marks object in the + * primary cx->fp chain. + */ + + if (oldfp && oldfp != down) { + PR_ASSERT(!oldfp->dormantNext); + oldfp->dormantNext = cx->dormantFrameChain; + cx->dormantFrameChain = oldfp; + } + cx->fp = &frame; ok = js_Interpret(cx, result); cx->fp = oldfp; + + if (oldfp && oldfp != down) { + PR_ASSERT(cx->dormantFrameChain == oldfp); + cx->dormantFrameChain = oldfp->dormantNext; + oldfp->dormantNext = NULL; + } + return ok; } @@ -850,9 +903,9 @@ ImportProperty(JSContext *cx, JSObject *obj, jsid id) * properties are accessed by opcodes using stack slot numbers * generated by the compiler rather than runtime name-lookup. These * local references, therefore, bypass the normal scope chain lookup. - * So, instead of defining a new property in the activation object, + * So, instead of defining a new property in the activation object, * modify the existing value in the stack slot. - */ + */ if (OBJ_GET_CLASS(cx, target) == &js_CallClass) { ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop); if (!ok) @@ -1015,19 +1068,21 @@ js_Interpret(JSContext *cx, jsval *result) if (rt->interruptHandler) { JSTrapHandler handler = (JSTrapHandler) rt->interruptHandler; - - switch (handler(cx, script, pc, &rval, - rt->interruptHandlerData)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - default:; - } + /* check copy of pointer for safety in multithreaded situation */ + if (handler) { + switch (handler(cx, script, pc, &rval, + rt->interruptHandlerData)) { + case JSTRAP_ERROR: + ok = JS_FALSE; + goto out; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + fp->rval = rval; + goto out; + default:; + } + } } switch (op) { @@ -1139,15 +1194,22 @@ js_Interpret(JSContext *cx, jsval *result) } \ } +#if JS_HAS_IN_OPERATOR case JSOP_IN: rval = POP(); - VALUE_TO_OBJECT(cx, rval, obj); + if (!JSVAL_IS_OBJECT(rval) || rval == JSVAL_NULL) { + JS_ReportError(cx, "target of 'in' operator must be an object"); + ok = JS_FALSE; + goto out; + } + obj = JSVAL_TO_OBJECT(rval); POP_ELEMENT_ID(id); ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto out; PUSH_OPND(BOOLEAN_TO_JSVAL(prop != NULL)); break; +#endif /* JS_HAS_IN_OPERATOR */ case JSOP_FORNAME: rval = POP(); @@ -1220,7 +1282,6 @@ js_Interpret(JSContext *cx, jsval *result) /* Is this the first iteration ? */ if (JSVAL_IS_VOID(rval)) { - /* Yes, create a new JSObject to hold the iterator state */ propobj = js_NewObject(cx, &prop_iterator_class, NULL, NULL); if (!propobj) { @@ -1228,7 +1289,7 @@ js_Interpret(JSContext *cx, jsval *result) goto out; } - /* + /* * Rewrite the iterator so we know to do the next case. * Do this before calling the enumerator, which could * displace cx->newborn and cause GC. @@ -1240,13 +1301,12 @@ js_Interpret(JSContext *cx, jsval *result) if (!ok) goto out; - /* + /* * Stash private iteration state into property iterator object. * NB: This code knows that the first slots are pre-allocated. */ PR_ASSERT(JS_INITIAL_NSLOTS >= 5); propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); - } else { /* This is not the first iteration. Recover iterator state. */ propobj = JSVAL_TO_OBJECT(rval); @@ -1255,14 +1315,12 @@ js_Interpret(JSContext *cx, jsval *result) } enum_next_property: - /* Get the next jsid to be enumerated and store it in rval */ OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &rval); propobj->slots[JSSLOT_ITR_STATE] = iter_state; /* No more jsids to iterate in obj ? */ if (iter_state == JSVAL_NULL) { - /* Enumerate the properties on obj's prototype chain */ obj = OBJ_GET_PROTO(cx, obj); if (!obj) { @@ -1283,7 +1341,6 @@ js_Interpret(JSContext *cx, jsval *result) /* Skip deleted and shadowed properties, leave next id in rval. */ if (obj != origobj) { - /* Have we already enumerated a clone of this property? */ ok = OBJ_LOOKUP_PROPERTY(cx, origobj, rval, &obj2, &prop); if (!ok) @@ -1303,7 +1360,8 @@ js_Interpret(JSContext *cx, jsval *result) /* Make sure rval is a string for uniformity and compatibility. */ if (!JSVAL_IS_INT(rval)) { rval = ATOM_KEY((JSAtom *)rval); - } else if (cx->version < JSVERSION_1_2) { + } else if ((cx->version <= JSVERSION_1_1) && + (cx->version >= JSVERSION_1_0)) { str = js_NumberToString(cx, (jsdouble) JSVAL_TO_INT(rval)); if (!str) { ok = JS_FALSE; @@ -1846,10 +1904,9 @@ js_Interpret(JSContext *cx, jsval *result) if (!ok) { cx->newborn[GCX_OBJECT] = NULL; #if JS_HAS_EXCEPTIONS - if (fp->exceptPending) { - /* throw expects to have the exception on the stack */ + if (fp->throwing) { sp[-1] = fp->exception; - goto throw; + goto do_throw; } #endif goto out; @@ -2067,10 +2124,10 @@ js_Interpret(JSContext *cx, jsval *result) RESTORE_SP(fp); if (!ok) { #if JS_HAS_EXCEPTIONS - if (fp->exceptPending) { - /* throw expects to have the exception on the stack */ + if (fp->throwing) { + /* do_throw expects to have the exception on the stack */ sp[-1] = fp->exception; - goto throw; + goto do_throw; } #endif goto out; @@ -2438,7 +2495,7 @@ js_Interpret(JSContext *cx, jsval *result) *vp = sp[-1]; break; -#ifdef JS_HAS_INITIALIZERS +#if JS_HAS_INITIALIZERS case JSOP_NEWINIT: argc = 0; fp->sharpDepth++; @@ -2527,32 +2584,41 @@ js_Interpret(JSContext *cx, jsval *result) #endif /* JS_HAS_INITIALIZERS */ #if JS_HAS_EXCEPTIONS + case JSOP_JSR: + len = GET_JUMP_OFFSET(pc); + PUSH(INT_TO_JSVAL(pc - script->code)); + CHECK_BRANCH(len); + break; + + case JSOP_RETSUB: + rval = POP(); + PR_ASSERT(JSVAL_IS_INT(rval)); + pc = script->code + JSVAL_TO_INT(rval) + 3 /* JSR */; + len = 0; + CHECK_BRANCH(-1); + break; + + case JSOP_EXCEPTION: + PUSH(fp->exception); + break; + case JSOP_THROW: - /* - * We enter here with an exception on the stack already, so we - * need to pop it off if we're leaving this frame, but we leave - * it on if we're jumping to a try section. - */ - PR_ASSERT(!fp->exceptPending); - throw: + do_throw: + fp->exception = POP(); tn = script->trynotes; offset = PTRDIFF(pc, script->code, jsbytecode); if (tn) { - for (; - tn->start && tn->end && (tn->start > offset || tn->end < offset); - tn++) - /* nop */ - ; - if (tn->catch) { - pc = script->code + tn->catch; + while (PR_UPTRDIFF(offset, tn->start) >= (pruword)tn->length) + tn++; + if (tn->catchStart) { + pc = script->code + tn->catchStart; len = 0; goto advance_pc; } } /* Not in a catch block, so propagate the exception. */ - fp->exceptPending = JS_TRUE; - fp->exception = POP(); + fp->throwing = JS_TRUE; ok = JS_FALSE; goto out; #endif /* JS_HAS_EXCEPTIONS */ @@ -2582,6 +2648,29 @@ js_Interpret(JSContext *cx, jsval *result) break; #endif /* JS_HAS_INSTANCEOF */ +#if JS_HAS_DEBUGGER_KEYWORD + case JSOP_DEBUGGER: + if (rt->debuggerHandler) { + JSTrapHandler handler = (JSTrapHandler) rt->debuggerHandler; + /* check copy of pointer for safety in multithread situation */ + if (handler) { + switch (handler(cx, script, pc, &rval, + rt->debuggerHandlerData)) { + case JSTRAP_ERROR: + ok = JS_FALSE; + goto out; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + fp->rval = rval; + goto out; + default:; + } + } + } + break; +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + default: JS_ReportError(cx, "unimplemented JavaScript bytecode %d", op); ok = JS_FALSE; @@ -2612,7 +2701,6 @@ js_Interpret(JSContext *cx, jsval *result) } #endif } - out: /* * Restore the previous frame's execution state. diff --git a/mozilla/js/src/jsinterp.h b/mozilla/js/src/jsinterp.h index 80d6cdfb328..47da4dcee65 100644 --- a/mozilla/js/src/jsinterp.h +++ b/mozilla/js/src/jsinterp.h @@ -48,8 +48,9 @@ struct JSStackFrame { JSPackedBool constructing; /* true if called via new operator */ uint8 overrides; /* bit-set of overridden Call properties */ JSPackedBool debugging; /* true if for JS_EvaluateInStackFrame */ - JSPackedBool exceptPending; /* is there an uncaught exception? */ - jsval exception; /* most-recently-thrown exception */ + JSPackedBool throwing; /* is there a pending exception? */ + jsval exception; /* most-recently-thrown exceptin */ + JSStackFrame *dormantNext; /* next dormant frame chain */ }; /* diff --git a/mozilla/js/src/jslock.c b/mozilla/js/src/jslock.c index 89a45448a12..92328d042d7 100644 --- a/mozilla/js/src/jslock.c +++ b/mozilla/js/src/jslock.c @@ -34,13 +34,13 @@ static PRLock *_global_lock; static void -js_LockGlobal() +js_LockGlobal() { PR_Lock(_global_lock); } static void -js_UnlockGlobal() +js_UnlockGlobal() { PR_Unlock(_global_lock); } @@ -113,8 +113,8 @@ swap [%1],%4 ba 3f mov 0,%0 2: mov 1,%0 -3:" - : "=r" (res) +3:" + : "=r" (res) : "r" (w), "r" (ov), "r" (nv), "r" (-1)); #else /* ULTRA_SPARC */ PR_ASSERT(ov != nv); @@ -124,8 +124,8 @@ cmp %2,%3 be,a 1f mov 1,%0 mov 0,%0 -1:" - : "=r" (res) +1:" + : "=r" (res) : "r" (w), "r" (ov), "r" (nv)); #endif /* ULTRA_SPARC */ return (int)res; @@ -135,7 +135,7 @@ mov 0,%0 PR_ASSERT(nv >= 0); #else PR_ASSERT(ov != nv); -#endif +#endif return compare_and_swap(w,ov,nv); #endif } @@ -230,7 +230,7 @@ js_DestroyLock(JSThinLock *p) static void js_Dequeue(JSThinLock *); PR_INLINE jsval -js_GetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot) +js_GetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot) { jsval v; #ifndef NSPR_LOCK @@ -265,7 +265,7 @@ js_GetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot) } PR_INLINE void -js_SetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot, jsval v) +js_SetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot, jsval v) { #ifndef NSPR_LOCK JSScope *scp = (JSScope *)obj->map; @@ -292,14 +292,14 @@ js_SetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot, jsval v) obj->slots[slot] = v; return; } -#endif +#endif js_LockObj(cx,obj); obj->slots[slot] = v; js_UnlockObj(cx,obj); } static JSFatLock * -mallocFatlock() +mallocFatlock() { JSFatLock *fl = (JSFatLock *)malloc(sizeof(JSFatLock)); /* for now */ PR_ASSERT(fl); @@ -312,14 +312,14 @@ mallocFatlock() } static void -freeFatlock(JSFatLock *fl) +freeFatlock(JSFatLock *fl) { - PR_DestroyLock(fl->slock); + PR_DestroyLock(fl->slock); PR_DestroyCondVar(fl->svar); free(fl); } -static int +static int js_SuspendThread(JSThinLock *p) { JSFatLock *fl = p->fat; @@ -332,7 +332,7 @@ js_SuspendThread(JSThinLock *p) PR_Unlock(fl->slock); return 1; } - stat = PR_WaitCondVar(fl->svar,PR_INTERVAL_NO_TIMEOUT); + stat = PR_WaitCondVar(fl->svar,PR_INTERVAL_NO_TIMEOUT); if (stat == PR_FAILURE) { fl->susp--; return 0; @@ -347,14 +347,14 @@ js_ResumeThread(JSThinLock *p) JSFatLock *fl = p->fat; PRStatus stat; PR_Lock(fl->slock); - fl->susp--; + fl->susp--; stat = PR_NotifyCondVar(fl->svar); PR_ASSERT(stat != PR_FAILURE); PR_Unlock(fl->slock); } -static JSFatLock * -listOfFatlocks(int l) +static JSFatLock * +listOfFatlocks(int l) { JSFatLock *m; JSFatLock *m0; @@ -370,7 +370,7 @@ listOfFatlocks(int l) } static void -deleteListOfFatlocks(JSFatLock *m) +deleteListOfFatlocks(JSFatLock *m) { JSFatLock *m0; for (; m; m=m0) { @@ -382,7 +382,7 @@ deleteListOfFatlocks(JSFatLock *m) static JSFatLockTable _fl_table; JSFatLock * -allocateFatlock() +allocateFatlock() { JSFatLock *m; @@ -404,8 +404,8 @@ allocateFatlock() return m; } -void -deallocateFatlock(JSFatLock *m) +void +deallocateFatlock(JSFatLock *m) { if (m == NULL) return; @@ -489,16 +489,16 @@ js_InitContextForLocking(JSContext *cx) Using p->slock as below (and correspondingly in js_SuspendThread()), js_SuspendThread() will notice that p->fat is empty, and hence return - immediately. + immediately. */ int -emptyFatlock(JSThinLock *p) +emptyFatlock(JSThinLock *p) { JSFatLock *fl = p->fat; int i; - + if (fl == NULL) return 1; PR_Lock(fl->slock); @@ -512,7 +512,7 @@ emptyFatlock(JSThinLock *p) return i < 1; } -/* +/* Fast locking and unlocking is implemented by delaying the allocation of a system lock (fat lock) until contention. As long as a locking thread A runs uncontended, the lock is represented solely @@ -608,7 +608,7 @@ js_Lock(JSThinLock *p, prword me) } PR_INLINE void -js_Unlock(JSThinLock *p, prword me) +js_Unlock(JSThinLock *p, prword me) { #ifdef DEBUG_ printf("\nT%x about to unlock %p\n",me,p); @@ -764,3 +764,4 @@ js_IsScopeLocked(JSScope *scope) #endif #endif /* JS_THREADSAFE */ + \ No newline at end of file diff --git a/mozilla/js/src/jsmath.c b/mozilla/js/src/jsmath.c index 369b4a205ac..c4404797229 100644 --- a/mozilla/js/src/jsmath.c +++ b/mozilla/js/src/jsmath.c @@ -233,9 +233,9 @@ math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * Math.random() support, lifted from java.util.Random.java. */ static void -random_setSeed(JSRuntime *rt, PRInt64 seed) +random_setSeed(JSRuntime *rt, int64 seed) { - PRInt64 tmp; + int64 tmp; LL_I2L(tmp, 1000); LL_DIV(seed, seed, tmp); @@ -246,7 +246,7 @@ random_setSeed(JSRuntime *rt, PRInt64 seed) static void random_init(JSRuntime *rt) { - PRInt64 tmp, tmp2; + int64 tmp, tmp2; /* Do at most once. */ if (rt->rngInitialized) @@ -277,7 +277,7 @@ random_init(JSRuntime *rt) static uint32 random_next(JSRuntime *rt, int bits) { - PRInt64 nextseed, tmp; + int64 nextseed, tmp; uint32 retval; LL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier); @@ -292,7 +292,7 @@ random_next(JSRuntime *rt, int bits) static jsdouble random_nextDouble(JSRuntime *rt) { - PRInt64 tmp, tmp2; + int64 tmp, tmp2; jsdouble d; LL_ISHL(tmp, random_next(rt, 27), 27); @@ -412,3 +412,4 @@ js_InitMathClass(JSContext *cx, JSObject *obj) return NULL; return proto; } + \ No newline at end of file diff --git a/mozilla/js/src/jsnum.c b/mozilla/js/src/jsnum.c index 924eb1bbac7..7cfd104cf4a 100644 --- a/mozilla/js/src/jsnum.c +++ b/mozilla/js/src/jsnum.c @@ -76,25 +76,22 @@ num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } static JSBool -num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) +num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; - jsdouble d, *dp; - jschar *ep; + jsdouble d; + const jschar *ep; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; - if (!js_strtod(str->chars, &ep, &d) || ep == str->chars) { + if (!js_strtod(cx, str->chars, &ep, &d)) + return JS_FALSE; + if (ep == str->chars) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - } else { - dp = js_NewDouble(cx, d); - if (!dp) - return JS_FALSE; - *rval = DOUBLE_TO_JSVAL(dp); + return JS_TRUE; } - return JS_TRUE; + return js_NewNumberValue(cx, d, rval); } /* See ECMA 15.1.2.2. */ @@ -103,114 +100,33 @@ num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsint radix; - const jschar *chars, *start; - jsdouble sum; - JSBool negative; - uintN digit, newDigit; - jschar c; - jschar digitMax = '9'; - jschar lowerCaseBound = 'a'; - jschar upperCaseBound = 'A'; - + jsdouble d; + const jschar *ep; + str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; - chars = str->chars; - - /* This assumes that the char strings are null-terminated - JS_ISSPACE will - * always evaluate to false at the end of a string containing only - * whitespace. - */ - while (JS_ISSPACE(*chars)) - chars++; - - if ((negative = (*chars == '-')) != 0 || *chars == '+') - chars++; if (argc > 1) { if (!js_ValueToECMAInt32(cx, argv[1], &radix)) return JS_FALSE; - } else { + } else radix = 0; - } - if (radix != 0) { - /* Some explicit radix was supplied */ - if (radix < 2 || radix > 36) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } else { - if (radix == 16 && *chars == '0' && - (*(chars + 1) == 'X' || *(chars + 1) == 'x')) - /* If radix is 16, ignore hex prefix. */ - chars += 2; - } - } else { - /* No radix supplied, or some radix that evaluated to 0. */ - if (*chars == '0') { - /* It's either hex or octal; only increment char if str isn't '0' */ - if (*(chars + 1) == 'X' || *(chars + 1) == 'x') /* Hex */ - { - chars += 2; - radix = 16; - } else { /* Octal */ - radix = 8; - } - } else { - radix = 10; /* Default to decimal. */ - } - } - - /* Done with the preliminaries; find some prefix of the string that's - * a number in the given radix. XXX might want to farm this out to - * a utility function, to share with the lexer's handling of - * literal constants. - */ - start = chars; /* Mark - if string is empty, we return NaN. */ - if (radix < 10) { - digitMax = (char) ('0' + radix - 1); - } else { - digitMax = '9'; - } - if (radix > 10) { - lowerCaseBound = (char) ('a' + radix - 10); - upperCaseBound = (char) ('A' + radix - 10); - } else { - lowerCaseBound = 'a'; - upperCaseBound = 'A'; - } - digit = 0; /* how many digits have we seen? (if radix == 10) */ - sum = 0; - while ((c = *chars) != 0) { /* 0 is never a valid character. */ - if ('0' <= c && c <= digitMax) - newDigit = c - '0'; - else if ('a' <= c && c < lowerCaseBound) - newDigit = c - 'a' + 10; - else if ('A' <= c && c < upperCaseBound) - newDigit = c - 'A' + 10; - else - break; - /* ECMA: 'But if R is 10 and Z contains more than 20 significant - * digits, every digit after the 20th may be replaced by a 0...' - * I'm not sure if doing this way is actually an improvement. - * XXX this may bear closer examination...... - */ - if (radix == 10 && ++digit > 20) - sum = sum*radix; - else - sum = sum*radix + newDigit; - chars++; - } - - if (chars == start) { + if (radix != 0 && (radix < 2 || radix > 36)) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - } else { - if (!js_NewNumberValue(cx, (negative ? -sum : sum), rval)) - return JS_FALSE; + return JS_TRUE; } - return JS_TRUE; + if (!js_strtointeger(cx, str->chars, &ep, radix, &d)) + return JS_FALSE; + if (ep == str->chars) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + return js_NewNumberValue(cx, d, rval); } + static JSFunctionSpec number_functions[] = { {"isNaN", num_isNaN, 1}, {"isFinite", num_isFinite, 1}, @@ -477,7 +393,6 @@ js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) { jsint i; - i = (jsint)d; if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { *rval = INT_TO_JSVAL(i); } else { @@ -511,7 +426,6 @@ js_NumberToString(JSContext *cx, jsdouble d) jsint i; char buf[32]; - i = (jsint)d; if (JSDOUBLE_IS_INT(d, i)) { PR_snprintf(buf, sizeof buf, "%ld", (long)i); } else { @@ -525,7 +439,7 @@ js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) { JSObject *obj; JSString *str; - jschar *ep; + const jschar *ep; jsdouble d; if (JSVAL_IS_OBJECT(v)) { @@ -544,8 +458,11 @@ js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) } else if (JSVAL_IS_STRING(v)) { str = JSVAL_TO_STRING(v); errno = 0; - if ((!js_strtod(str->chars, &ep, &d) || *ep != 0) && - (!js_strtol(str->chars, &ep, 0, &d) || *ep != 0)) { + /* Note that ECMAScript doesn't treat numbers beginning with a zero as octal numbers here. + * This works because all such numbers will be interpreted as decimal by js_strtod and + * will never get passed to js_strtointeger, which would interpret them as octal. */ + if ((!js_strtod(cx, str->chars, &ep, &d) || js_SkipWhiteSpace(ep) != str->chars + str->length) && + (!js_strtointeger(cx, str->chars, &ep, 0, &d) || js_SkipWhiteSpace(ep) != str->chars + str->length)) { goto badstr; } *dp = d; @@ -695,60 +612,214 @@ js_DoubleToInteger(jsdouble d) return neg ? -d : d; } -/* XXXbe rewrite me! */ -JSBool -js_strtod(const jschar *s, jschar **ep, jsdouble *dp) -{ - size_t i, n; - char *cstr, *estr; - jsdouble d; - n = js_strlen(s); - cstr = malloc(n + 1); +JSBool +js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp) +{ + size_t i; + char *cstr, *istr, *estr; + JSBool negative; + jsdouble d; + const jschar *s1 = js_SkipWhiteSpace(s); + size_t length = js_strlen(s1); + + cstr = malloc(length + 1); if (!cstr) return JS_FALSE; - for (i = 0; i <= n; i++) { - if (s[i] >> 8) { - free(cstr); - return JS_FALSE; + for (i = 0; i <= length; i++) { + if (s1[i] >> 8) { + cstr[i] = 0; + break; } - cstr[i] = (char)s[i]; + cstr[i] = (char)s1[i]; } - errno = 0; - d = PR_strtod(cstr, &estr); + + istr = cstr; + if ((negative = (*istr == '-')) != 0 || *istr == '+') + istr++; + if (!strncmp(istr, "Infinity", 8)) { + d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity); + estr = istr + 8; + } else { + errno = 0; + d = PR_strtod(cstr, &estr); + if (errno == ERANGE) + if (d == HUGE_VAL) + d = *cx->runtime->jsPositiveInfinity; + else if (d == -HUGE_VAL) + d = *cx->runtime->jsNegativeInfinity; + } + free(cstr); - if (errno == ERANGE) - return JS_FALSE; - *ep = (jschar *)s + (estr - cstr); + i = estr - cstr; + *ep = i ? s1 + i : s; *dp = d; return JS_TRUE; } -/* XXXbe rewrite me! */ -JSBool -js_strtol(const jschar *s, jschar **ep, jsint base, jsdouble *dp) +struct BinaryDigitReader { - size_t i, n; - char *cstr, *estr; - long l; + uintN base; /* Base of number; must be a power of 2 */ + uintN digit; /* Current digit value in radix given by base */ + uintN digitMask; /* Mask to extract the next bit from digit */ + const jschar *digits; /* Pointer to the remaining digits */ + const jschar *end; /* Pointer to first non-digit */ +}; - n = js_strlen(s); - cstr = malloc(n + 1); - if (!cstr) - return JS_FALSE; - for (i = 0; i <= n; i++) { - if (s[i] >> 8) { - free(cstr); - return JS_FALSE; - } - cstr[i] = (char)s[i]; +/* Return the next binary digit from the number or -1 if done */ +static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr) +{ + intN bit; + + if (bdr->digitMask == 0) { + uintN c; + + if (bdr->digits == bdr->end) + return -1; + + c = *bdr->digits++; + if ('0' <= c && c <= '9') + bdr->digit = c - '0'; + else if ('a' <= c && c <= 'z') + bdr->digit = c - 'a' + 10; + else bdr->digit = c - 'A' + 10; + bdr->digitMask = bdr->base >> 1; + } + bit = (bdr->digit & bdr->digitMask) != 0; + bdr->digitMask >>= 1; + return bit; +} + +JSBool +js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp) +{ + JSBool negative; + jsdouble value; + const jschar *start; + const jschar *s1 = js_SkipWhiteSpace(s); + + if ((negative = (*s1 == '-')) != 0 || *s1 == '+') + s1++; + + if (base == 0) + /* No base supplied, or some base that evaluated to 0. */ + if (*s1 == '0') + /* It's either hex or octal; only increment char if str isn't '0' */ + if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */ + s1 += 2; + base = 16; + } else /* Octal */ + base = 8; + else + base = 10; /* Default to decimal. */ + else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) + /* If base is 16, ignore hex prefix. */ + s1 += 2; + + /* Done with the preliminaries; find some prefix of the string that's + * a number in the given base. + */ + start = s1; /* Mark - if string is empty, we return NaN. */ + value = 0.0; + while (1) { + uintN digit; + jschar c = *s1; + if ('0' <= c && c <= '9') + digit = c - '0'; + else if ('a' <= c && c <= 'z') + digit = c - 'a' + 10; + else if ('A' <= c && c <= 'Z') + digit = c - 'A' + 10; + else + break; + if (digit >= (uintN)base) + break; + value = value*base + digit; + s1++; + } + + if (value >= 9007199254740992.0) + if (base == 10) { + /* If we're accumulating a decimal number and the number is >= 2^53, then + * the result from the repeated multiply-add above may be inaccurate. Call + * PR_strtod to get the correct answer. + */ + size_t i; + size_t length = s1 - start; + char *cstr = malloc(length + 1); + char *estr; + + if (!cstr) + return JS_FALSE; + for (i = 0; i != length; i++) + cstr[i] = (char)start[i]; + cstr[length] = 0; + + errno = 0; + value = PR_strtod(cstr, &estr); + if (errno == ERANGE && value == HUGE_VAL) + value = *cx->runtime->jsPositiveInfinity; + free(cstr); + + } else if (base == 2 || base == 4 || base == 8 || base == 16 || base == 32) { + /* The number may also be inaccurate for one of these bases. This + * happens if the addition in value*base + digit causes a round-down + * to an even least significant mantissa bit when the first dropped bit + * is a one. If any of the following digits in the number (which haven't + * been added in yet) are nonzero then the correct action would have + * been to round up instead of down. An example of this occurs when + * reading the number 0x1000000000000081, which rounds to 0x1000000000000000 + * instead of 0x1000000000000100. + */ + struct BinaryDigitReader bdr; + intN bit, bit2; + intN j; + + bdr.base = base; + bdr.digitMask = 0; + bdr.digits = start; + bdr.end = s1; + value = 0.0; + + /* Skip leading zeros. */ + do { + bit = GetNextBinaryDigit(&bdr); + } while (bit == 0); + + if (bit == 1) { + /* Gather the 53 significant bits (including the leading 1) */ + value = 1.0; + for (j = 52; j; j--) { + bit = GetNextBinaryDigit(&bdr); + if (bit < 0) + goto done; + value = value*2 + bit; + } + /* bit2 is the 54th bit (the first dropped from the mantissa) */ + bit2 = GetNextBinaryDigit(&bdr); + if (bit2 >= 0) { + jsdouble factor = 2.0; + intN sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */ + intN bit3; + + while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) { + sticky |= bit3; + factor *= 2; + } + value += bit2 & (bit | sticky); + value *= factor; + } + done:; + } + } + /* We don't worry about inaccurate numbers for any other base. */ + + if (s1 == start) { + *dp = 0.0; + *ep = s; + } else { + *dp = negative ? -value : value; + *ep = s1; } - errno = 0; - l = strtol(cstr, &estr, base); - free(cstr); - if (errno == ERANGE) - return JS_FALSE; - *ep = (jschar *)s + (estr - cstr); - *dp = (jsdouble)l; return JS_TRUE; } diff --git a/mozilla/js/src/jsnum.h b/mozilla/js/src/jsnum.h index 6b52d408352..bee89f49dd3 100644 --- a/mozilla/js/src/jsnum.h +++ b/mozilla/js/src/jsnum.h @@ -57,14 +57,14 @@ PR_BEGIN_EXTERN_C #define JSDOUBLE_IS_NEGZERO(d) (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \ JSDOUBLE_LO32(d) == 0) -#define JSDOUBLE_IS_INT_2(d, i) (!JSDOUBLE_IS_NEGZERO(d) && (jsdouble)i == d) - -#ifdef XP_PC -/* XXX MSVC miscompiles NaN floating point comparisons for ==, !=, <, and <= */ -#define JSDOUBLE_IS_INT(d, i) (!JSDOUBLE_IS_NaN(d) && JSDOUBLE_IS_INT_2(d, i)) -#else -#define JSDOUBLE_IS_INT(d, i) JSDOUBLE_IS_INT_2(d, i) -#endif +/* + * JSDOUBLE_IS_INT first checks that d is neither NaN nor infinite, to avoid + * raising SIGFPE on platforms such as Alpha Linux, then (only if the cast is + * safe) leaves i as (jsint)d. This also avoid anomalous NaN floating point + * comparisons under MSVC. + */ +#define JSDOUBLE_IS_INT(d, i) (JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d) \ + && ((d) == (i = (jsint)(d)))) /* Initialize the Number class, returning its prototype object. */ extern JSObject * @@ -140,11 +140,27 @@ js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); extern jsdouble js_DoubleToInteger(jsdouble d); +/* + * Similar to strtod except that replaces overflows with infinities of the correct + * sign and underflows with zeros of the correct sign. Guaranteed to return the + * closest double number to the given input in dp. + * Also allows inputs of the form [+|-]Infinity, which produce an infinity of the + * appropriate sign. The case of the "Infinity" string must match. + * If the string does not have a number in it, set *ep to s and return 0.0 in dp. + * Return false if out of memory. + */ extern JSBool -js_strtod(const jschar *s, jschar **ep, jsdouble *dp); +js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp); +/* + * Similar to strtol except that handles integers of arbitrary size. Guaranteed to + * return the closest double number to the given input when radix is 10 or a power of 2. + * May experience roundoff errors for very large numbers of a different radix. + * If the string does not have a number in it, set *ep to s and return 0.0 in dp. + * Return false if out of memory. + */ extern JSBool -js_strtol(const jschar *s, jschar **ep, jsint radix, jsdouble *dp); +js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint radix, jsdouble *dp); PR_END_EXTERN_C diff --git a/mozilla/js/src/jsobj.c b/mozilla/js/src/jsobj.c index 884a527f815..52812c0ba74 100644 --- a/mozilla/js/src/jsobj.c +++ b/mozilla/js/src/jsobj.c @@ -278,7 +278,7 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, hash = js_hash_object(obj); hep = PR_HashTableRawLookup(table, hash, obj); he = *hep; - + /* * It's possible that the value of a property has changed from the * first time the object's properties are traversed (when the property @@ -1081,11 +1081,9 @@ FindConstructor(JSContext *cx, const char *name, jsval *vp) } } - if(!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty **) &sprop)) - { + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop)) return JS_FALSE; - } - if(!sprop) { + if (!sprop) { *vp = JSVAL_VOID; return JS_TRUE; } @@ -1241,24 +1239,33 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) #define CHECK_FOR_EMPTY_INDEX(id) /* nothing */ #endif +/* JSVAL_INT_MAX as a string */ +#define JSVAL_INT_MAX_STRING "1073741823" + #define CHECK_FOR_FUNNY_INDEX(id) \ PR_BEGIN_MACRO \ - if (!JSVAL_IS_INT(id)) { \ + if (!JSVAL_IS_INT(id)) { \ JSAtom *_atom = (JSAtom *)id; \ JSString *_str = ATOM_TO_STRING(_atom); \ const jschar *_cp = _str->chars; \ - if (JS7_ISDEC(*_cp)) { \ - jsint _index = JS7_UNDEC(*_cp); \ - _cp++; \ - if (_index != 0) { \ - while (JS7_ISDEC(*_cp)) { \ - _index = 10 * _index + JS7_UNDEC(*_cp); \ - if (_index < 0) \ - break; \ - _cp++; \ - } \ - } \ - if (*_cp == 0 && INT_FITS_IN_JSVAL(_index)) \ + if (JS7_ISDEC(*_cp) && \ + _str->length <= sizeof(JSVAL_INT_MAX_STRING)-1) \ + { \ + jsuint _index = JS7_UNDEC(*_cp++); \ + jsuint _oldIndex = 0; \ + jsuint _c; \ + if (_index != 0) { \ + while (JS7_ISDEC(*_cp)) { \ + _oldIndex = _index; \ + _c = JS7_UNDEC(*_cp); \ + _index = 10*_index + _c; \ + _cp++; \ + } \ + } \ + if (*_cp == 0 && \ + (_oldIndex < (JSVAL_INT_MAX / 10) || \ + (_oldIndex == (JSVAL_INT_MAX / 10) && \ + _c < (JSVAL_INT_MAX % 10)))) \ id = INT_TO_JSVAL(_index); \ } else { \ CHECK_FOR_EMPTY_INDEX(id); \ @@ -1266,6 +1273,7 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) } \ PR_END_MACRO + JSBool js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, @@ -1275,7 +1283,8 @@ js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSScope *scope; JSScopeProperty *sprop; - /* Handle old bug that treated empty string as zero index. */ + /* Handle old bug that treated empty string as zero index. + * Also convert string indices to numbers if applicable. */ CHECK_FOR_FUNNY_INDEX(id); /* Lock if object locking is required by this implementation. */ @@ -1345,7 +1354,8 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSObject *obj2, *proto; JSScopeProperty *sprop; - /* Handle old bug that treated empty string as zero index. */ + /* Handle old bug that treated empty string as zero index. + * Also convert string indices to numbers if applicable. */ CHECK_FOR_FUNNY_INDEX(id); /* Search scopes starting with obj and following the prototype link. */ @@ -1529,6 +1539,9 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp) ; if (fp) obj = js_GetCallObject(cx, fp, parent, withobj); + } else if (clasp == &js_CallClass) { + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + fun = fp->fun; } #endif @@ -1546,7 +1559,8 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop)) return JS_FALSE; if (!sprop) { - /* Handle old bug that treated empty string as zero index. */ + /* Handle old bug that treated empty string as zero index. + * Also convert string indices to numbers if applicable. */ CHECK_FOR_FUNNY_INDEX(id); #if JS_BUG_NULL_INDEX_PROPS @@ -1611,7 +1625,8 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) return JS_FALSE; } - /* Handle old bug that treated empty string as zero index. */ + /* Handle old bug that treated empty string as zero index. + * Also convert string indices to numbers if applicable. */ CHECK_FOR_FUNNY_INDEX(id); hash = js_HashValue(id); @@ -1743,7 +1758,7 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) pval = LOCKED_OBJ_GET_SLOT(obj, slot); /* Evil overloaded operator assign() hack. */ - if (JSVAL_IS_OBJECT(pval)) { + if ((!JSVERSION_IS_ECMA(cx->version)) && (JSVAL_IS_OBJECT(pval))) { assignobj = JSVAL_TO_OBJECT(pval); if (assignobj) { older = JS_SetErrorReporter(cx, NULL); @@ -1883,7 +1898,8 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID; - /* Handle old bug that treated empty string as zero index. */ + /* Handle old bug that treated empty string as zero index. + * Also convert string indices to numbers if applicable. */ CHECK_FOR_FUNNY_INDEX(id); if (!js_LookupProperty(cx, obj, id, &proto, &prop)) @@ -2061,6 +2077,7 @@ JSBool js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { + JSObject *proto_obj; JSClass *clasp; JSEnumerateOp enumerate; JSScopeProperty *sprop; @@ -2073,7 +2090,7 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, enumerate = clasp->enumerate; if (clasp->flags & JSCLASS_NEW_ENUMERATE) return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); - + switch (enum_op) { case JSENUMERATE_INIT: @@ -2088,24 +2105,40 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, */ JS_LOCK_OBJ(cx, obj); scope = (JSScope *) obj->map; - for (sprop = scope->props; sprop; sprop = sprop->next) { - if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols) - length++; - } - ida = js_NewIdArray(cx, length); - if (!ida) { - JS_UNLOCK_OBJ(cx, obj); - goto init_error; - } - i = 0; - for (sprop = scope->props; sprop; sprop = sprop->next) { - if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols) { - PR_ASSERT(i < length); - ida->vector[i++] = sym_id(sprop->symbols); + + /* + * If this object shares a scope with its prototype, don't enumerate + * its properties. Otherwise they will be enumerated a second time + * when the prototype object is enumerated. + */ + proto_obj = OBJ_GET_PROTO(cx, obj); + if (proto_obj && (scope == (JSScope *)proto_obj->map)) { + ida = js_NewIdArray(cx, 0); + if (!ida) { + JS_UNLOCK_OBJ(cx, obj); + goto init_error; } - } + } else { + /* Object has a private scope; Enumerate all props in scope. */ + for (sprop = scope->props; sprop; sprop = sprop->next) { + if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols) + length++; + } + ida = js_NewIdArray(cx, length); + if (!ida) { + JS_UNLOCK_OBJ(cx, obj); + goto init_error; + } + i = 0; + for (sprop = scope->props; sprop; sprop = sprop->next) { + if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols) { + PR_ASSERT(i < length); + ida->vector[i++] = sym_id(sprop->symbols); + } + } + } JS_UNLOCK_OBJ(cx, obj); - + state = JS_malloc(cx, sizeof(JSNativeIteratorState)); if (!state) { JS_DestroyIdArray(cx, ida); @@ -2117,7 +2150,7 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, if (idp) *idp = INT_TO_JSVAL(length); return JS_TRUE; - + case JSENUMERATE_NEXT: state = JSVAL_TO_PRIVATE(*statep); ida = state->ida; @@ -2443,3 +2476,59 @@ out: } #endif /* JS_HAS_XDR */ + +#ifdef DEBUG + +/* Routines to print out values during debugging. */ + +void printChar(jschar *cp) { + fprintf(stderr, "jschar* (0x%x) \"", cp); + while (*cp) + fputc(*cp++, stderr); + fputc('"', stderr); + fputc('\n', stderr); +} + +void printString(JSString *str) { + jsuint i; + fprintf(stderr, "string (0x%x) \"", str); + for (i=0; i < str->length; i++) + fputc(str->chars[i], stderr); + fputc('"', stderr); + fputc('\n', stderr); +} + +void printVal(jsval val) { + fprintf(stderr, "val %d (0x%x) = ", val, val); + if (JSVAL_IS_NULL(val)) { + fprintf(stderr, "null\n"); + } else if (JSVAL_IS_VOID(val)) { + fprintf(stderr, "undefined\n"); + } else if (JSVAL_IS_OBJECT(val)) { + /* XXX can do more here */ + fprintf(stderr, "object\n"); + } else if (JSVAL_IS_INT(val)) { + fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val)); + } else if (JSVAL_IS_STRING(val)) { + printString(JSVAL_TO_STRING(val)); + } else if (JSVAL_IS_DOUBLE(val)) { + fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val)); + } else { + PR_ASSERT(JSVAL_IS_BOOLEAN(val)); + fprintf(stderr, "(boolean) %s\n", + JSVAL_TO_BOOLEAN(val) ? "true" : "false"); + } + fflush(stderr); +} + +void printId(jsid id) { + fprintf(stderr, "id %d (0x%x) is ", id, id); + printVal(js_IdToValue(id)); +} + +void printAtom(JSAtom *atom) { + printString(ATOM_TO_STRING(atom)); +} + +#endif + diff --git a/mozilla/js/src/jsopcode.c b/mozilla/js/src/jsopcode.c index 7634c841303..d6d8a4f4ed0 100644 --- a/mozilla/js/src/jsopcode.c +++ b/mozilla/js/src/jsopcode.c @@ -417,7 +417,7 @@ js_NewPrinter(JSContext *cx, const char *name, uintN indent) map = obj->map; if (map->ops == &js_ObjectOps) { if (OBJ_GET_CLASS(cx, obj) == &js_CallClass) { - obj = fp->fun->object; + obj = fp->fun ? fp->fun->object : NULL; if (obj) map = obj->map; } @@ -819,37 +819,35 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) js_printf(jp, "\t}\n"); break; - case SRC_TRY: - js_printf(jp, "\ttry {\n"); + case SRC_TRYFIN: + if (js_GetSrcNoteOffset(sn, 0) == 0) { + js_printf(jp, "\ttry {\n"); + } else { + jp->indent -= 4; + js_printf(jp, "\t} finally {\n"); + } jp->indent += 4; break; case SRC_CATCH: jp->indent -= 4; + sn = js_GetSrcNote(jp->script, pc); pc += oplen; js_printf(jp, "\t} catch ("); /* balance) */ - sn = js_GetSrcNote(jp->script, pc); - len = js_CodeSpec[*pc].length; - switch (*pc) { - case JSOP_SETVAR: - lval = ATOM_BYTES(GetSlotAtom(jp->scope, js_GetLocalVariable, - GET_VARNO(pc))); - goto do_catchvar; - case JSOP_SETARG: - lval = ATOM_BYTES(GetSlotAtom(jp->scope, js_GetArgument, - GET_ARGNO(pc))); - goto do_catchvar; - case JSOP_SETNAME: - lval = ATOM_BYTES(GET_ATOM(cx, jp->script, pc)); - do_catchvar: - js_printf(jp, "%s%s", - (sn && SN_TYPE(sn) == SRC_VAR ? "var " : ""), lval); - break; - default: - PR_ASSERT(0); + pc += 6; /* name Object, pushobj, exception */ + js_printf(jp, "%s", ATOM_BYTES(GET_ATOM(cx, + jp->script, pc))); + len = js_GetSrcNoteOffset(sn, 0); + pc += 4; /* initprop, enterwith */ + if (len) { + js_printf(jp, " if "); + DECOMPILE_CODE(pc, len - 3); /* don't decompile ifeq */ + js_printf(jp, "%s", POP_STR()); + pc += len; } js_printf(jp, ") {\n"); /* balance} */ jp->indent += 4; + len = 0; break; case SRC_FUNCDEF: { @@ -885,6 +883,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = Sprint(&ss->sprinter, ""); break; + case JSOP_JSR: + case JSOP_RETSUB: + todo = -2; + break; + + case JSOP_EXCEPTION: + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + todo = -2; + break; + case JSOP_POP: case JSOP_POPV: sn = js_GetSrcNote(jp->script, pc); @@ -938,10 +947,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_ENTERWITH: + sn = js_GetSrcNote(jp->script, pc); + todo = -2; + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + break; rval = POP_STR(); js_printf(jp, "\twith (%s) {\n", rval); jp->indent += 4; - todo = -2; break; case JSOP_LEAVEWITH: @@ -963,9 +975,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_THROW: + sn = js_GetSrcNote(jp->script, pc); + todo = -2; + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + break; rval = POP_STR(); js_printf(jp, "\t%s %s;\n", cs->name, rval); - todo = -2; break; case JSOP_GOTO: @@ -1695,7 +1710,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = -2; break; -#ifdef JS_HAS_INITIALIZERS +#if JS_HAS_INITIALIZERS case JSOP_NEWINIT: LOCAL_ASSERT(ss->top >= 2); (void) PopOff(ss, op); @@ -1767,6 +1782,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) #endif /* JS_HAS_SHARP_VARS */ #endif /* JS_HAS_INITIALIZERS */ +#if JS_HAS_DEBUGGER_KEYWORD + case JSOP_DEBUGGER: + js_printf(jp, "\tdebugger;\n"); + todo = -2; + break; +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + default: todo = -2; break; diff --git a/mozilla/js/src/jsopcode.def b/mozilla/js/src/jsopcode.def index 3733aa6268e..3e561425978 100644 --- a/mozilla/js/src/jsopcode.def +++ b/mozilla/js/src/jsopcode.def @@ -190,3 +190,13 @@ OPDEF(JSOP_THROW, 110,"throw", NULL, 1, 1, 0, 0, JOF_BYTE) /* 'in' and 'instanceof' ops. */ OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 6, JOF_BYTE) OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE) + +/* debugger op */ +OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* jsr/return for finally handling */ +OPDEF(JSOP_JSR, 114,"jsr", NULL, 3, 0, 1, 0, JOF_JUMP) +OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 1, 0, 0, JOF_BYTE) + +/* more exception handling ops */ +OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE) diff --git a/mozilla/js/src/jsparse.c b/mozilla/js/src/jsparse.c index 5d6587ceafa..616d5c52a61 100644 --- a/mozilla/js/src/jsparse.c +++ b/mozilla/js/src/jsparse.c @@ -69,6 +69,9 @@ typedef JSParseNode * JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); +typedef JSParseNode * +JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowCallSyntax); static JSParser FunctionStmt; #if JS_HAS_LEXICAL_CLOSURE @@ -91,7 +94,7 @@ static JSParser ShiftExpr; static JSParser AddExpr; static JSParser MulExpr; static JSParser UnaryExpr; -static JSParseNode *MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCallSyntax); +static JSMemberParser MemberExpr; static JSParser PrimaryExpr; /* @@ -160,7 +163,7 @@ WellTerminated(JSContext *cx, JSTokenStream *ts, JSTokenType lastExprType) if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { #if JS_HAS_LEXICAL_CLOSURE if ((tt == TOK_FUNCTION || lastExprType == TOK_FUNCTION) && - cx->version < JSVERSION_1_2) { + (cx->version < JSVERSION_1_2) && (cx->version >= JSVERSION_1_0)) { return JS_TRUE; } #endif @@ -244,6 +247,8 @@ out: return ok; } +#ifdef CHECK_RETURN_EXPR + /* * Insist on a final return before control flows out of pn, but don't be too * smart about loops (do {...; return e2;} while(0) at the end of a function @@ -286,6 +291,8 @@ CheckFinalReturn(JSParseNode *pn) static char badreturn_str[] = "function does not always return a value"; +#endif /* CHECK_RETURN_EXPR */ + static JSParseNode * FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, JSTreeContext *tc) @@ -373,10 +380,10 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSAtom *funAtom, *argAtom; JSObject *parent; JSFunction *fun, *outerFun; - JSStmtInfo *topStmt; JSBool ok, named; JSObject *pobj; JSScopeProperty *sprop; + JSTreeContext funtc; jsval junk; /* Make a TOK_FUNCTION node. */ @@ -395,10 +402,6 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, if (!parent) return NULL; - /* Clear tc->topStmt for semantic checking (restore at label out:). */ - topStmt = tc->topStmt; - tc->topStmt = NULL; - #if JS_HAS_LEXICAL_CLOSURE if (!funAtom || cx->fp->scopeChain != parent || InWithStatement(tc)) { /* Don't name the function if enclosed by a with statement or equiv. */ @@ -485,7 +488,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, ok = JS_FALSE; goto out); pn->pn_pos.begin = ts->token.pos.begin; - pn2 = FunctionBody(cx, ts, fun, tc); + INIT_TREE_CONTEXT(&funtc); + pn2 = FunctionBody(cx, ts, fun, &funtc); if (!pn2) { ok = JS_FALSE; goto out; @@ -497,7 +501,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn->pn_fun = fun; pn->pn_body = pn2; - pn->pn_tryCount = tc->tryCount; + pn->pn_tryCount = funtc.tryCount; #if JS_HAS_LEXICAL_CLOSURE if (outerFun || cx->fp->scopeChain != parent || InWithStatement(tc)) @@ -510,7 +514,6 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, ok = JS_TRUE; out: - tc->topStmt = topStmt; if (!ok) { if (named) (void) OBJ_DELETE_PROPERTY(cx, parent, (jsid)funAtom, &junk); @@ -986,59 +989,41 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn1 = NULL; } else { /* Set pn1 to a var list or an initializing expression. */ +#if JS_HAS_IN_OPERATOR + /* + * Set the TCF_IN_FOR_INIT flag during parsing of the first clause + * of the for statement. This flag will will be used by the + * RelExpr production; if it flag is set, then the 'in' keyword + * will not be recognized as an operator, leaving it available to + * be parsed as part of a for/in loop. A side effect of this + * restriction is that (unparenthesized) expressions involving an + * 'in' operator are illegal in the init clause of an ordinary for + * loop. + */ + tc->flags |= TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ if (tt == TOK_VAR) { (void) js_GetToken(cx, ts); pn1 = Variables(cx, ts, tc); } else { pn1 = Expr(cx, ts, tc); } +#if JS_HAS_IN_OPERATOR + tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ if (!pn1) return NULL; } /* - * There are three kinds of for/in syntax (see ECMA 12.6.3): - * 1. for (i in o) ... - * 2. for (var i in o) ... - * 3. for (var i = e in o) ... - * The 'var i = e in o' in 3 will be parsed as a variable declaration - * with an 'in' expression as its initializer, leaving ts->pushback at - * the right parenthesis. This condition tests 1, then 3, then 2: - */ - if (pn1 && - (pn1->pn_type == TOK_IN || - (pn1->pn_type == TOK_VAR && ts->pushback.type == TOK_RP) || - js_MatchToken(cx, ts, TOK_IN))) { + * We can be sure that if it's a for/in loop, there's still an 'in' + * keyword here, even if Javascript recognizes it as an operator, + * because we've excluded it from parsing by setting the + * TCF_IN_FOR_INIT flag on the JSTreeContext argument. + */ + if (pn1 && js_MatchToken(cx, ts, TOK_IN)) { stmtInfo.type = STMT_FOR_IN_LOOP; - switch (pn1->pn_type) { - case TOK_IN: - pn2 = pn1; - pn1 = pn1->pn_left; - break; - case TOK_VAR: - if (pn1->pn_count != 1) { - js_ReportCompileError(cx, ts, "invalid for/in variables"); - return NULL; - } - pn2 = pn1->pn_head->pn_expr; - if (pn2) { - /* pn1 is 'var i = e' -- must pop e before loop. */ - pn1->pn_op = JSOP_POP; - if (pn2->pn_type == TOK_IN) { - /* Found 'var i = e in o' -- make 'in' be the root. */ - PR_ASSERT(ts->pushback.type == TOK_RP); - pn1->pn_head->pn_expr = pn2->pn_left; - pn2->pn_left = pn1; - break; - } - } - /* FALL THROUGH */ - default: - pn2 = NULL; - break; - } - /* Check that the left side of the 'in' is valid. */ if (pn1->pn_type != TOK_VAR && pn1->pn_type != TOK_NAME && @@ -1049,11 +1034,9 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } /* Parse the object expression as the right operand of 'in'. */ - if (!pn2) { - pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc)); - if (!pn2) - return NULL; - } + pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc)); + if (!pn2) + return NULL; pn->pn_left = pn2; } else { /* Parse the loop condition or null into pn2. */ @@ -1102,114 +1085,108 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return pn; #if JS_HAS_EXCEPTIONS - case TOK_TRY: + case TOK_TRY: { + JSParseNode *catchtail = NULL; /* * try nodes are ternary. * kid1 is the try Statement * kid2 is the catch node * kid3 is the finally Statement + * + * catch nodes are ternary. + * kid1 is the discriminant + * kid2 is the next catch node, or NULL + * kid3 is the catch block (on kid3 so that we can always append a + * new catch pn on catchtail->kid2) + * + * catch discriminant nodes are binary + * atom is the receptacle + * expr is the discriminant code + * + * finally nodes are unary (just the finally expression) */ pn = NewParseNode(cx, &ts->token, PN_TERNARY); - pn1 = Statement(cx, ts, tc); - if (!pn1) + pn->pn_op = JSOP_NOP; + + MUST_MATCH_TOKEN(TOK_LC, "missing { after before try block"); + /* } balance */ + pn->pn_kid1 = Statements(cx, ts, tc); + if (!pn->pn_kid1) return NULL; - if (js_PeekToken(cx, ts) == TOK_CATCH) { - pn2 = NewParseNode(cx, &ts->token, PN_BINARY); - if (!pn2) + /* { balance */ + MUST_MATCH_TOKEN(TOK_RC, "missing } after try block"); + + pn->pn_kid2 = NULL; + catchtail = pn; + while(js_PeekToken(cx, ts) == TOK_CATCH) { + /* + * legal catch forms are: + * catch (v) + * catch (v if ) + */ + + if (!catchtail->pn_kid1->pn_expr) { + js_ReportCompileError(cx, ts, + "catch clause after general catch"); return NULL; - (void)js_GetToken(cx, ts); - MUST_MATCH_TOKEN(TOK_LP, "missing ( after catch"); /* balance) */ - tt = js_PeekToken(cx, ts); - if (tt == TOK_VAR) { - (void)js_GetToken(cx, ts); - pn4 = Variables(cx, ts, tc); - if (!pn4) - return NULL; - if (pn4->pn_count != 1) { - js_ReportCompileError(cx, ts, - "only one variable declaration permitted in catch declaration"); - return NULL; - } - } else { - pn4 = Expr(cx, ts, tc); - if (!pn4) - return NULL; - /* restrict the expression to instanceof */ - if (pn4->pn_op != JSOP_INSTANCEOF && - pn4->pn_op != JSOP_NAME && - pn4->pn_op != JSOP_GETARG && - pn4->pn_op != JSOP_GETVAR) { - js_ReportCompileError(cx, ts, - "catch conditional must be instanceof or variable declaration"); - PR_ASSERT(0); - return NULL; - } } - /* rewrite the declaration/conditional expr as appropriate */ - switch(pn4->pn_type) { - case TOK_NAME: - switch(pn4->pn_op) { - case JSOP_NAME: - pn4->pn_op = JSOP_SETNAME; - break; - case JSOP_GETARG: - pn4->pn_op = JSOP_SETARG; - break; - default: - PR_ASSERT(0); - } - break; - case TOK_VAR: - PR_ASSERT(pn4->pn_head->pn_type == TOK_NAME); - switch(pn4->pn_head->pn_op) { - case JSOP_GETVAR: - pn4->pn_head->pn_op = JSOP_SETVAR; - break; - case JSOP_GETARG: - pn4->pn_head->pn_op = JSOP_SETARG; - break; - case JSOP_NAME: - pn4->pn_head->pn_op = JSOP_SETNAME; - case JSOP_NOP: - break; - default: - PR_ASSERT(0); - } - break; - case TOK_INSTANCEOF: - PR_ASSERT(0); - default: - PR_ASSERT(0); + /* catch node */ + pn2 = NewParseNode(cx, &ts->token, PN_TERNARY); + pn2->pn_op = JSOP_NOP; + /* + * We use a PN_NAME for the discriminant (catchguard) node + * with the actual discriminant code in the initializer spot + */ + pn3 = NewParseNode(cx, &ts->token, PN_NAME); + if (!pn2 || !pn3) + return NULL; + + (void)js_GetToken(cx, ts); /* eat `catch' */ + + MUST_MATCH_TOKEN(TOK_LP, "missing ( after catch"); /* balance) */ + MUST_MATCH_TOKEN(TOK_NAME, "missing identifier in catch"); + pn3->pn_atom = ts->token.t_atom; + if (js_PeekToken(cx, ts) == TOK_COLON) { + (void)js_GetToken(cx, ts); /* eat `:' */ + pn3->pn_expr = Expr(cx, ts, tc); + if (!pn3->pn_expr) + return NULL; + } else { + pn3->pn_expr = NULL; } - pn2->pn_left = pn4; - - /* (balance: */ - MUST_MATCH_TOKEN(TOK_RP, "missing ) after catch declaration"); - pn2->pn_right = Statement(cx, ts, tc); - if (!pn2->pn_right) + pn2->pn_kid1 = pn3; + + /* ( balance: */ + MUST_MATCH_TOKEN(TOK_RP, "missing ) after catch"); + + MUST_MATCH_TOKEN(TOK_LC, "missing { before catch block"); + pn2->pn_kid3 = Statements(cx, ts, tc); + if (!pn2->pn_kid3) return NULL; - } else { - pn2 = NULL; + MUST_MATCH_TOKEN(TOK_RC, "missing } after catch block"); + + catchtail = catchtail->pn_kid2 = pn2; } + catchtail->pn_kid2 = NULL; + if (js_MatchToken(cx, ts, TOK_FINALLY)) { - pn3 = Statement(cx, ts, tc); - if (!pn3) + MUST_MATCH_TOKEN(TOK_LC, "missing { before finally block"); + pn->pn_kid3 = Statements(cx, ts, tc); + if (!pn->pn_kid3) return NULL; + MUST_MATCH_TOKEN(TOK_RC, "missing } after finally block"); } else { - pn3 = NULL; + pn->pn_kid3 = NULL; } - if (!pn2 && !pn3) { + if (!pn->pn_kid2 && !pn->pn_kid3) { js_ReportCompileError(cx, ts, "missing catch or finally after try"); return NULL; } tc->tryCount++; - pn->pn_kid1 = pn1; - pn->pn_kid2 = pn2; - pn->pn_kid3 = pn3; return pn; - + } case TOK_THROW: pn = NewParseNode(cx, &ts->token, PN_UNARY); if (!pn) @@ -1225,6 +1202,16 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_op = JSOP_THROW; pn->pn_kid = pn2; break; + + /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ + case TOK_CATCH: + js_ReportCompileError(cx, ts, "catch without try"); + return NULL; + + case TOK_FINALLY: + js_ReportCompileError(cx, ts, "finally without try"); + return NULL; + #endif /* JS_HAS_EXCEPTIONS */ case TOK_BREAK: @@ -1365,11 +1352,13 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_kid = NULL; } +#ifdef CHECK_RETURN_EXPR if ((tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == (TCF_RETURN_EXPR | TCF_RETURN_VOID)) { js_ReportCompileError(cx, ts, badreturn_str); return NULL; } +#endif break; case TOK_LC: @@ -1392,6 +1381,17 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_kid = NULL; return pn; +#if JS_HAS_DEBUGGER_KEYWORD + case TOK_DEBUGGER: + if(!WellTerminated(cx, ts, TOK_ERROR)) + return NULL; + pn = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn) + return NULL; + pn->pn_type = TOK_DEBUGGER; + return pn; +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + case TOK_ERROR: return NULL; @@ -1463,6 +1463,17 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSScopeProperty *sprop; JSBool ok; + /* + * The tricky part of this code is to create special + * parsenode opcodes for getting and setting variables + * (which will be stored as special slots in the frame). + * The complex special case is an eval() inside a + * function. If the evaluated string references variables in + * the enclosing function, then we need to generate + * the special variable opcodes. + * We determine this by looking up the variable id in the + * current variable scope. + */ PR_ASSERT(ts->token.type == TOK_VAR); pn = NewParseNode(cx, &ts->token, PN_LIST); if (!pn) @@ -1475,8 +1486,13 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return NULL; clasp = OBJ_GET_CLASS(cx, obj); if (fun && clasp == &js_FunctionClass) { + /* We are compiling code inside a function */ getter = js_GetLocalVariable; setter = js_SetLocalVariable; + } else if (fun && clasp == &js_CallClass) { + /* We are compiling code from an eval inside a function */ + getter = js_GetCallVariable; + setter = js_SetCallVariable; } else { getter = clasp->getProperty; setter = clasp->setProperty; @@ -1517,7 +1533,25 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) PR_ASSERT(sprop->getter == js_GetLocalVariable); PR_ASSERT(JSVAL_IS_INT(sprop->id) && JSVAL_TO_INT(sprop->id) < fun->nvars); - } + } else if (clasp == &js_CallClass) { + if (sprop->getter == js_GetCallVariable) { + /* + * Referencing a variable introduced by a var + * statement in the enclosing function. Check + * that the slot number we have is in range. + */ + PR_ASSERT(JSVAL_IS_INT(sprop->id) && + JSVAL_TO_INT(sprop->id) < fun->nvars); + } else { + /* + * A variable introduced through another eval: + * don't use the special getters and setters + * since we can't allocate a slot in the frame. + */ + getter = sprop->getter; + setter = sprop->setter; + } + } } else { /* Global var: (re-)set id a la js_DefineProperty. */ sprop->id = ATOM_KEY(atom); @@ -1528,11 +1562,22 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) sprop->attrs &= ~JSPROP_READONLY; } } else { + /* + * Property not found in current variable scope: we have not + * seen this variable before. + * Define a new variable by adding a property to the current + * scope, or by allocating more slots in the function's frame. + */ sprop = NULL; if (prop) { OBJ_DROP_PROPERTY(cx, pobj, prop); prop = NULL; } + if (getter == js_GetCallVariable) { + /* Can't increase fun->nvars in an active frame! */ + getter = clasp->getProperty; + setter = clasp->setProperty; + } ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID, getter, setter, JSPROP_ENUMERATE | JSPROP_PERMANENT, @@ -1540,9 +1585,14 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (ok && prop) { pobj = obj; if (getter == js_GetLocalVariable) { + /* + * Allocate more room for variables in the + * function's frame. We can do this only + * before the function is called. + */ sprop = (JSScopeProperty *)prop; sprop->id = INT_TO_JSVAL(fun->nvars++); - } + } } } @@ -1560,21 +1610,28 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } } - if (ok && fun && clasp == &js_FunctionClass && !InWithStatement(tc)) { - PR_ASSERT(sprop); + if (ok && fun && (clasp == &js_FunctionClass || + clasp == &js_CallClass) && + !InWithStatement(tc)) + { + /* Depending on the value of the getter, change the + * opcodes to the forms for arguments and variables. + */ if (getter == js_GetArgument) { PR_ASSERT(sprop && JSVAL_IS_INT(sprop->id)); pn2->pn_op = (pn2->pn_op == JSOP_NAME) ? JSOP_GETARG : JSOP_SETARG; pn2->pn_slot = JSVAL_TO_INT(sprop->id); - } else if (getter == js_GetLocalVariable) { + } else if (getter == js_GetLocalVariable || + getter == js_GetCallVariable) + { PR_ASSERT(sprop && JSVAL_IS_INT(sprop->id)); pn2->pn_op = (pn2->pn_op == JSOP_NAME) ? JSOP_GETVAR : JSOP_SETVAR; pn2->pn_slot = JSVAL_TO_INT(sprop->id); - } + } } if (prop) @@ -1611,16 +1668,20 @@ Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return pn; } -/* ZZZbe don't create functions till codegen? or at least don't bind fn name */ +/* ZZZbe don't create functions till codegen? or at least don't bind + * fn name */ static JSBool LookupArgOrVar(JSContext *cx, JSAtom *atom, JSTreeContext *tc, JSOp *opp, jsint *slotp) { JSObject *obj, *pobj; + JSClass *clasp; + JSFunction *fun; JSScopeProperty *sprop; - obj = cx->fp->scopeChain; - if (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) + obj = js_FindVariableScope(cx, &fun); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp != &js_FunctionClass && clasp != &js_CallClass) return JS_TRUE; if (InWithStatement(tc)) return JS_TRUE; @@ -1632,7 +1693,9 @@ LookupArgOrVar(JSContext *cx, JSAtom *atom, JSTreeContext *tc, if (sprop->getter == js_GetArgument) { *opp = JSOP_GETARG; *slotp = JSVAL_TO_INT(sprop->id); - } else if (sprop->getter == js_GetLocalVariable) { + } else if (sprop->getter == js_GetLocalVariable || + sprop->getter == js_GetCallVariable) + { *opp = JSOP_GETVAR; *slotp = JSVAL_TO_INT(sprop->id); } @@ -1683,6 +1746,9 @@ static JSParseNode * CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn1, *pn2, *pn3; +#if JS_HAS_IN_OPERATOR + uintN oldflags; +#endif /* JS_HAS_IN_OPERATOR */ pn = OrExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_HOOK)) { @@ -1690,7 +1756,20 @@ CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn = NewParseNode(cx, &ts->token, PN_TERNARY); if (!pn) return NULL; +#if JS_HAS_IN_OPERATOR + /* + * Always accept the 'in' operator in the middle clause of a ternary, + * where it's unambiguous, even if we might be parsing the init of a + * for statement. + */ + oldflags = tc->flags; + tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ pn2 = AssignExpr(cx, ts, tc); +#if JS_HAS_IN_OPERATOR + tc->flags = oldflags; +#endif /* JS_HAS_IN_OPERATOR */ + if (!pn2) return NULL; MUST_MATCH_TOKEN(TOK_COLON, "missing : in conditional expression"); @@ -1781,21 +1860,40 @@ RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSParseNode *pn; JSTokenType tt; JSOp op; +#if JS_HAS_IN_OPERATOR + uintN inForInitFlag; + + inForInitFlag = tc->flags & TCF_IN_FOR_INIT; + /* + * Uses of the in operator in ShiftExprs are always unambiguous, + * so unset the flag that prohibits recognizing it. + */ + tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ pn = ShiftExpr(cx, ts, tc); while (pn && (js_MatchToken(cx, ts, TOK_RELOP) #if JS_HAS_IN_OPERATOR - || js_MatchToken(cx, ts, TOK_IN) -#endif + /* + * Only recognize the 'in' token as an operator if we're not + * currently in the init expr of a for loop. + */ + || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) +#endif /* JS_HAS_IN_OPERATOR */ #if JS_HAS_INSTANCEOF || js_MatchToken(cx, ts, TOK_INSTANCEOF) -#endif +#endif /* JS_HAS_INSTANCEOF */ )) { tt = ts->token.type; op = ts->token.t_op; pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc)); } +#if JS_HAS_IN_OPERATOR + /* Restore previous state of inForInit flag. */ + tc->flags |= inForInitFlag; +#endif /* JS_HAS_IN_OPERATOR */ + return pn; } @@ -1998,7 +2096,8 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } static JSParseNode * -ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *listNode) +ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSParseNode *listNode) { JSBool matched; @@ -2020,17 +2119,18 @@ ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *l } static JSParseNode * -MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCallSyntax) +MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowCallSyntax) { JSParseNode *pn, *pn2, *pn3; JSTokenType tt; - /* Check for new expressions */ + /* Check for new expression first. */ ts->flags |= TSF_REGEXP; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_REGEXP; if (tt == TOK_NEW) { - (void)js_GetToken(cx, ts); + (void) js_GetToken(cx, ts); pn = NewParseNode(cx, &ts->token, PN_LIST); if (!pn) @@ -2050,7 +2150,6 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCall return NULL; } pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - } else { pn = PrimaryExpr(cx, ts, tc); if (!pn) @@ -2279,10 +2378,26 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif /* JS_HAS_INITIALIZERS */ case TOK_LP: + { +#if JS_HAS_IN_OPERATOR + uintN oldflags; +#endif pn = NewParseNode(cx, &ts->token, PN_UNARY); if (!pn) return NULL; +#if JS_HAS_IN_OPERATOR + /* + * Always accept the 'in' operator in a parenthesized expression, + * where it's unambiguous, even if we might be parsing the init of a + * for statement. + */ + oldflags = tc->flags; + tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ pn2 = Expr(cx, ts, tc); +#if JS_HAS_IN_OPERATOR + tc->flags = oldflags; +#endif /* JS_HAS_IN_OPERATOR */ if (!pn2) return NULL; @@ -2292,9 +2407,12 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_pos.end = ts->token.pos.end; pn->pn_kid = pn2; break; + } case TOK_STRING: +#if JS_HAS_SHARP_VARS notsharp = JS_TRUE; +#endif /* JS_HAS_SHARP_VARS */ /* FALL THROUGH */ case TOK_NAME: case TOK_OBJECT: @@ -2317,7 +2435,9 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (!pn) return NULL; pn->pn_dval = ts->token.t_dval; +#if JS_HAS_SHARP_VARS notsharp = JS_TRUE; +#endif /* JS_HAS_SHARP_VARS */ break; case TOK_PRIMARY: @@ -2325,7 +2445,9 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (!pn) return NULL; pn->pn_op = ts->token.t_op; +#if JS_HAS_SHARP_VARS notsharp = JS_TRUE; +#endif /* JS_HAS_SHARP_VARS */ break; #if !JS_HAS_EXPORT_IMPORT @@ -2358,7 +2480,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) defsharp->pn_kid = pn; return defsharp; } -#endif +#endif /* JS_HAS_SHARP_VARS */ return pn; } diff --git a/mozilla/js/src/jspubtd.h b/mozilla/js/src/jspubtd.h index 1bc735806f6..01d2d5a1b73 100644 --- a/mozilla/js/src/jspubtd.h +++ b/mozilla/js/src/jspubtd.h @@ -31,7 +31,7 @@ # elif defined(__GNUC__) # define PR_INLINE inline # else -# define PR_INLINE +# define PR_INLINE # endif #endif /* PR_INLINE */ @@ -163,7 +163,7 @@ typedef struct JSXDRState JSXDRState; #ifdef XP_OS2 #define CRT_CALL _Optlink #else -#define CRT_CALL +#define CRT_CALL #endif #endif @@ -177,7 +177,7 @@ typedef JSBool * JSObject. The behavior depends on the value of enum_op: * * JSENUMERATE_INIT - A new, opaque iterator state should be allocated and - * stored in *statep. (You can use PRIVATE_TO_JSVAL() to store + * stored in *statep. (You can use PRIVATE_TO_JSVAL() to store * a pointer in *statep). The number of properties that will be * enumerated should be returned as an integer jsval in *idp, if idp * is non-NULL. @@ -291,3 +291,4 @@ typedef void JSErrorReport *report); #endif /* jspubtd_h___ */ + \ No newline at end of file diff --git a/mozilla/js/src/jsregexp.c b/mozilla/js/src/jsregexp.c index 3124af89905..4bd88235726 100644 --- a/mozilla/js/src/jsregexp.c +++ b/mozilla/js/src/jsregexp.c @@ -1997,6 +1997,22 @@ js_NewRegExp(JSContext *cx, JSString *str, uintN flags) goto out; } +#ifdef DEBUG_notme + { + /* print the compiled regexp program bytecode */ + jsuint i; + for (i=0; i < state.progLength; i++) { + int b = (int) re->program[i]; + fprintf(stderr, "%d", b); + if ((i > 0 && i % 8 == 0) || i == state.progLength-1) + fprintf(stderr, "\n"); + else + fprintf(stderr, ", "); + } + fprintf(stderr, "\n"); + } +#endif + /* Success: lock re->source string. */ (void) js_LockGCThing(cx, str); out: diff --git a/mozilla/js/src/jsscan.c b/mozilla/js/src/jsscan.c index 3d24e188c43..a83619678b3 100644 --- a/mozilla/js/src/jsscan.c +++ b/mozilla/js/src/jsscan.c @@ -131,10 +131,14 @@ static struct keyword { #endif #ifdef RESERVE_ECMA_KEYWORDS - {"debugger", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3}, {"enum", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3}, #endif +#if JS_HAS_DEBUGGER_KEYWORD + {"debugger", TOK_DEBUGGER, JSOP_NOP}, /* XXX version? */ +#elif defined(RESERVE_ECMA_KEYWORDS) + {"debugger", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3}, +#endif {0} }; @@ -253,8 +257,17 @@ static void SendSourceToJSDebugger(JSTokenStream *ts, jschar *str, size_t length) { if (!ts->jsdsrc) { - ts->jsdsrc = JSD_NewSourceText(ts->jsdc, - ts->filename ? ts->filename : "typein"); + const char* filename = ts->filename ? ts->filename : "typein"; + + if (1 == ts->lineno) { + ts->jsdsrc = JSD_NewSourceText(ts->jsdc, filename); + } else { + ts->jsdsrc = JSD_FindSourceForURL(ts->jsdc, filename); + if (ts->jsdsrc && JSD_SOURCE_PARTIAL != + JSD_GetSourceStatus(ts->jsdc, ts->jsdsrc)) { + ts->jsdsrc = NULL; + } + } } if (ts->jsdsrc) { /* here we convert our Unicode into a C string to pass to JSD */ @@ -320,7 +333,7 @@ GetChar(JSTokenStream *ts) } } for (j = 0; i < len; i++, j++) - ubuf[i] = (jschar) cbuf[j]; + ubuf[i] = (jschar) (unsigned char) cbuf[j]; ts->userbuf.limit = ubuf + len; ts->userbuf.ptr = ubuf; } else @@ -668,7 +681,7 @@ retry: if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) { jsint radix; - jschar *endptr; + const jschar *endptr; jsdouble dval; radix = 10; @@ -683,7 +696,15 @@ retry: RETURN(TOK_ERROR); c = GetChar(ts); radix = 16; - } else if (JS7_ISDEC(c)) { + } else if (JS7_ISDEC(c) && c < '8') { + /* + * XXX Warning needed. Checking against c < '8' above is + * non-ECMA, but is required to support legacy code; it's + * likely that "08" and "09" are in use in code having to do + * with dates. So we need to support it, which makes our + * behavior a superset of ECMA in this area. We should be + * raising a warning if '8' or '9' is encountered. + */ radix = 8; } } @@ -729,16 +750,13 @@ retry: FINISH_TOKENBUF(&ts->tokenbuf); if (radix == 10) { - /* Let js_strtod() do the hard work and validity checks. */ - if (!js_strtod(ts->tokenbuf.base, &endptr, &dval)) { - js_ReportCompileError(cx, ts, - "floating point literal out of range"); + if (!js_strtod(cx, ts->tokenbuf.base, &endptr, &dval)) { + js_ReportCompileError(cx, ts, "out of memory"); RETURN(TOK_ERROR); } } else { - /* Let js_strtol() do the hard work, then check for overflow */ - if (!js_strtol(ts->tokenbuf.base, &endptr, radix, &dval)) { - js_ReportCompileError(cx, ts, "integer literal too large"); + if (!js_strtointeger(cx, ts->tokenbuf.base, &endptr, radix, &dval)) { + js_ReportCompileError(cx, ts, "out of memory"); RETURN(TOK_ERROR); } } diff --git a/mozilla/js/src/jsscan.h b/mozilla/js/src/jsscan.h index 2695b687f7b..870c383b599 100644 --- a/mozilla/js/src/jsscan.h +++ b/mozilla/js/src/jsscan.h @@ -91,6 +91,7 @@ typedef enum JSTokenType { TOK_FINALLY = 57, /* finally keyword */ TOK_THROW = 58, /* throw keyword */ TOK_INSTANCEOF = 59, /* instanceof keyword */ + TOK_DEBUGGER = 60, /* debugger keyword */ TOK_RESERVED, /* reserved keywords */ TOK_LIMIT /* domain size */ } JSTokenType; diff --git a/mozilla/js/src/jsscript.h b/mozilla/js/src/jsscript.h index e654813cee5..2ce818db29a 100644 --- a/mozilla/js/src/jsscript.h +++ b/mozilla/js/src/jsscript.h @@ -27,29 +27,31 @@ PR_BEGIN_EXTERN_C /* - * Exception handling information. - * All fields are code offsets, relative to the beginning of the script. + * Exception handling runtime information. + * + * All fields except length are code offsets, relative to the beginning of + * the script. If script->trynotes is not null, it points to a vector of + * these structs terminated by one with start, catchStart, and finallyStart + * all equal to 0, and length == script->length. */ - struct JSTryNote { - ptrdiff_t start; /* beginning of try{} region */ - ptrdiff_t end; /* end of try{} region */ - ptrdiff_t catch; /* beginning of catch{} (backptr during CG) */ - ptrdiff_t finally; /* beginning of finally handler */ + ptrdiff_t start; /* start of try statement */ + ptrdiff_t length; /* count of try statement bytecodes */ + ptrdiff_t catchStart; /* start of catch block (0 if none) */ }; struct JSScript { - jsbytecode *code; /* bytecodes and their immediate operands */ - uint32 length; /* length of code vector */ - JSAtomMap atomMap; /* maps immediate index to literal struct */ - const char *filename; /* source filename or null */ - uintN lineno; /* base line number of script */ - uintN depth; /* maximum stack depth in slots */ - jssrcnote *notes; /* line number and other decompiling data */ - JSTryNote *trynotes; /* exception table for this script */ - JSPrincipals *principals; /* principals for this script */ - void *javaData; /* XXX extra data used by jsjava.c */ - JSObject *object; /* optional Script-class object wrapper */ + jsbytecode *code; /* bytecodes and their immediate operands */ + uint32 length; /* length of code vector */ + JSAtomMap atomMap; /* maps immediate index to literal struct */ + const char *filename; /* source filename or null */ + uintN lineno; /* base line number of script */ + uintN depth; /* maximum stack depth in slots */ + jssrcnote *notes; /* line number and other decompiling data */ + JSTryNote *trynotes; /* exception table for this script */ + JSPrincipals *principals; /* principals for this script */ + void *javaData; /* XXX extra data used by jsjava.c */ + JSObject *object; /* optional Script-class object wrapper */ }; extern JSClass js_ScriptClass; diff --git a/mozilla/js/src/jsstr.c b/mozilla/js/src/jsstr.c index f906a8068a0..fadcc18b6ee 100644 --- a/mozilla/js/src/jsstr.c +++ b/mozilla/js/src/jsstr.c @@ -73,7 +73,7 @@ static const unsigned char netCharType[256] = /* Bit 0 xalpha -- the alphas -** Bit 1 xpalpha -- as xalpha but +** Bit 1 xpalpha -- as xalpha but ** converts spaces to plus and plus to %20 ** Bit 2 ... path -- as xalphas but doesn't escape '/' */ @@ -110,7 +110,7 @@ str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(d) || - (mask = (jsint)d) != d || + (mask = (jsint)d) != d || mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)) { JS_ReportError(cx, "invalid string escape mask %x", mask); @@ -313,10 +313,10 @@ str_resolve(JSContext *cx, JSObject *obj, jsval id) if (!JSVAL_IS_INT(id)) return JS_TRUE; + slot = JSVAL_TO_INT(id); str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; - slot = JSVAL_TO_INT(id); if ((size_t)slot >= str->length) return JS_TRUE; return str_resolve1(cx, obj, str, slot); @@ -2242,6 +2242,15 @@ js_strchr(const jschar *s, jschar c) return NULL; } +const jschar * +js_SkipWhiteSpace(const jschar *s) +{ + /* JS_ISSPACE is false on a null. */ + while (JS_ISSPACE(*s)) + s++; + return s; +} + jschar * js_strncpy(jschar *t, const jschar *s, size_t n) { @@ -2262,7 +2271,7 @@ js_InflateString(JSContext *cx, const char *bytes, size_t length) if (!chars) return NULL; for (i = 0; i < length; i++) - chars[i] = (jschar) (unsigned char) bytes[i]; + chars[i] = (unsigned char) bytes[i]; chars[i] = 0; return chars; } diff --git a/mozilla/js/src/jsstr.h b/mozilla/js/src/jsstr.h index b96701c2f5d..64d41d60ca1 100644 --- a/mozilla/js/src/jsstr.h +++ b/mozilla/js/src/jsstr.h @@ -232,6 +232,12 @@ js_strchr(const jschar *s, jschar c); extern jschar * js_strncpy(jschar *t, const jschar *s, size_t n); +/* + * Return s advanced past any Unicode white space characters. + */ +extern const jschar * +js_SkipWhiteSpace(const jschar *s); + /* * Inflate bytes to JS chars and vice versa. Report out of memory via cx * and return null on error, otherwise return the jschar or byte vector that diff --git a/mozilla/js/src/jsxdrapi.c b/mozilla/js/src/jsxdrapi.c index f033d069139..f238da13cc4 100644 --- a/mozilla/js/src/jsxdrapi.c +++ b/mozilla/js/src/jsxdrapi.c @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 8; c-basic-offset: 4 -*- +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in @@ -417,7 +417,7 @@ JS_XDRDouble(JSXDRState *xdr, jsdouble **dp) } return JS_TRUE; } - + JS_PUBLIC_API(JSBool) JS_XDRValue(JSXDRState *xdr, jsval *vp) { @@ -455,13 +455,13 @@ JS_XDRValue(JSXDRState *xdr, jsval *vp) break; } case JSVAL_BOOLEAN: { - uint32 bool; + uint32 b; if (xdr->mode == JSXDR_ENCODE) - bool = (uint32)JSVAL_TO_BOOLEAN(*vp); - if (!JS_XDRUint32(xdr, &bool)) + b = (uint32)JSVAL_TO_BOOLEAN(*vp); + if (!JS_XDRUint32(xdr, &b)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) - *vp = BOOLEAN_TO_JSVAL((JSBool)bool); + *vp = BOOLEAN_TO_JSVAL((JSBool)b); break; } case JSVAL_VOID: @@ -469,6 +469,16 @@ JS_XDRValue(JSXDRState *xdr, jsval *vp) return JS_FALSE; break; default: + if (type & JSVAL_INT) { + uint32 i; + if (xdr->mode == JSXDR_ENCODE) + i = JSVAL_TO_INT(*vp); + if (!JS_XDRUint32(xdr, &i)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = INT_TO_JSVAL(i); + break; + } JS_ReportError(xdr->cx, "unknown jsval type %#lx for XDR", type); return JS_FALSE; } diff --git a/mozilla/js/src/jsxdrapi.h b/mozilla/js/src/jsxdrapi.h index d77778fe514..e507f09e4bb 100644 --- a/mozilla/js/src/jsxdrapi.h +++ b/mozilla/js/src/jsxdrapi.h @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 8; c-basic-offset: 4 -*- +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in diff --git a/mozilla/js/src/prconv.sed b/mozilla/js/src/prconv.sed index 65e8904889c34d4312d45fae516717b2b8288977..e562292e3e2340c423965bd18fc95abc8fbbbd31 100644 GIT binary patch delta 29 VcmaFF`iONx(#DcJCLsn4003@21ZV&N delta 22 ccmaFF`iONx5_gH2iGDzkXC9E=7+uN)09>{QlK=n!