diff --git a/mozilla/js/macbuild/JavaScriptPPC.mcp b/mozilla/js/macbuild/JavaScriptPPC.mcp index 38e4d04fd9e..83560548553 100644 Binary files a/mozilla/js/macbuild/JavaScriptPPC.mcp and b/mozilla/js/macbuild/JavaScriptPPC.mcp differ diff --git a/mozilla/js/macbuild/JavaScriptPPCDebug.mcp b/mozilla/js/macbuild/JavaScriptPPCDebug.mcp index 6b393231608..98067572564 100644 Binary files a/mozilla/js/macbuild/JavaScriptPPCDebug.mcp and b/mozilla/js/macbuild/JavaScriptPPCDebug.mcp differ diff --git a/mozilla/js/src/Makefile b/mozilla/js/src/Makefile index f2e178a19f1..3759fcafa20 100644 --- a/mozilla/js/src/Makefile +++ b/mozilla/js/src/Makefile @@ -31,8 +31,7 @@ endif MODULE = js -CSRCS = prmjtime.c \ - jsapi.c \ +CSRCS = jsapi.c \ jsarray.c \ jsatom.c \ jsbool.c \ @@ -54,6 +53,8 @@ CSRCS = prmjtime.c \ jsscript.c \ jsstr.c \ jslock.c \ + jsxdrapi.c \ + prmjtime.c \ $(NULL) EXPORTS = jsapi.h \ @@ -83,8 +84,18 @@ EXPORTS = jsapi.h \ jsscope.h \ jsscript.h \ jsstr.h \ + jsxdrapi.h \ $(NULL) +ASFILES = $(wildcard *_$(OS_ARCH).s) + +JS_SAFE_ARENA = 1 + +ifdef JS_SAFE_ARENA +DEFINES += -DJS_USE_SAFE_ARENA +CSRCS += prarena.c +endif + include $(DEPTH)/config/rules.mk export:: install @@ -101,10 +112,32 @@ ifdef JS_THREADSAFE DEFINES += -DJS_THREADSAFE endif +ifdef JS_NO_THIN_LOCKS +DEFINES += -DJS_USE_ONLY_NSPR_LOCKS +endif + ifdef JS_VERSION DEFINES += -DJS_VERSION=$(JS_VERSION) endif +ifeq ($(CPU_ARCH),sparc) + +ifndef JS_NO_ULTRA +ULTRA_OPTIONS := -xarch=v8plus,-DULTRA_SPARC +ULTRA_OPTIONSCC := -DULTRA_SPARC +else +ULTRA_OPTIONS := -xarch=v8 +ULTRA_OPTIONSCC := +endif + +ifeq ($(shell uname -m),sun4u) +ASFLAGS += -Wa,$(ULTRA_OPTIONS),-P,-L,-D_ASM,-D__STDC__=0 $(ULTRA_OPTIONSCC) +else +ASFLAGS += -Wa,-xarch=v8,-P,-L,-D_ASM,-D__STDC__=0 +endif + +endif # sparc + INCLUDES += -I. ifdef NSPR20 diff --git a/mozilla/js/src/commfiles b/mozilla/js/src/commfiles index 14efb5933a2..3f7b4a8b4b1 100644 --- a/mozilla/js/src/commfiles +++ b/mozilla/js/src/commfiles @@ -23,6 +23,7 @@ jsgc.h jsinterp.c jsinterp.h jslock.h +jslock.c jsmath.c jsmath.h jsnum.c @@ -46,5 +47,5 @@ jsscript.c jsscript.h jsstr.c jsstr.h -jsxdrapi.c jsxdrapi.h +jsxdrapi.c diff --git a/mozilla/js/src/js.c b/mozilla/js/src/js.c index e5b746e5615..abbcdd5af07 100644 --- a/mozilla/js/src/js.c +++ b/mozilla/js/src/js.c @@ -45,6 +45,17 @@ #include "jsscope.h" #include "jsscript.h" +#ifdef LIVECONNECT +#include "jsjava.h" +#endif + +#ifdef JSDEBUGGER +#include "jsdebug.h" +#ifdef JSDEBUGGER_JAVA_UI +#include "jsdjava.h" +#endif /* JSDEBUGGER_JAVA_UI */ +#endif /* JSDEBUGGER */ + #ifdef XP_UNIX #include #include @@ -60,60 +71,66 @@ # error "JSFILE must be defined for this module to work." #endif +#ifdef JSDEBUGGER +static JSDContext *_jsdc; +#ifdef JSDEBUGGER_JAVA_UI +static JSDJContext *_jsdjc; +#endif /* JSDEBUGGER_JAVA_UI */ +#endif /* JSDEBUGGER */ + static void Process(JSContext *cx, JSObject *obj, char *filename) { - JSScript script; /* XXX we know all about this struct */ JSTokenStream *ts; JSCodeGenerator cg; + JSBool ok; + JSScript *script; jsval result; JSString *str; - memset(&script, 0, sizeof script); - script.filename = filename; - if (filename && strcmp(filename, "-") != 0) { - ts = js_NewFileTokenStream(cx, filename); - } else { - ts = js_NewBufferTokenStream(cx, NULL, 0); - if (ts) ts->file = stdin; - } - if (!ts) goto out; + ts = js_NewFileTokenStream(cx, filename, stdin); + if (!ts) + goto out; +#ifdef JSDEBUGGER + if (!filename) + ts->filename = "typein"; +#endif if (isatty(fileno(ts->file))) - ts->flags |= (TSF_INTERACTIVE | TSF_COMMAND); + ts->flags |= TSF_INTERACTIVE; - js_InitCodeGenerator(cx, &cg, &cx->codePool); - script.notes = NULL; - while (!(ts->flags & (TSF_ERROR | TSF_EOF))) { - script.lineno = ts->lineno; - if (ts->flags & TSF_INTERACTIVE) - printf("js> "); - - CG_RESET(&cg); - if (!js_Parse(cx, obj, ts, &cg) || CG_OFFSET(&cg) == 0) - continue; - - script.code = cg.base; - script.length = CG_OFFSET(&cg); - script.depth = cg.maxStackDepth; - if (js_InitAtomMap(cx, &script.atomMap, &cg.atomList)) { - script.notes = js_FinishTakingSrcNotes(cx, &cg); - if (script.notes) { - if (JS_ExecuteScript(cx, obj, &script, &result) && + do { + js_InitCodeGenerator(cx, &cg, ts->filename, ts->lineno, ts->principals); + do { + if (ts->flags & TSF_INTERACTIVE) + printf("js> "); + ok = js_CompileTokenStream(cx, obj, ts, &cg); + if (ts->flags & TSF_ERROR) { + ts->flags &= ~TSF_ERROR; + CLEAR_PUSHBACK(ts); + ok = JS_TRUE; + } + } while (ok && !(ts->flags & TSF_EOF) && CG_OFFSET(&cg) == 0); + if (ok) { + script = js_NewScriptFromCG(cx, &cg, NULL); + if (script) { + if (JS_ExecuteScript(cx, obj, script, &result) && (ts->flags & TSF_INTERACTIVE) && result != JSVAL_VOID) { str = JS_ValueToString(cx, result); if (str) printf("%s\n", JS_GetStringBytes(str)); } - free(script.notes); + JS_DestroyScript(cx, script); } - js_FreeAtomMap(cx, &script.atomMap); } - } + cg.firstLine = ts->lineno; + js_ResetCodeGenerator(cx, &cg); + RESET_TOKENBUF(ts); + } while (!(ts->flags & TSF_EOF)); out: if (ts) - (void) js_CloseTokenStream(cx, ts); + (void) js_CloseTokenStream(cx, ts); PR_FreeArenaPool(&cx->codePool); PR_FreeArenaPool(&cx->tempPool); } @@ -185,12 +202,15 @@ Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { +#ifdef LIVECONNECT + JSJ_SimpleShutdown(); +#endif exit(0); return JS_FALSE; } #ifdef GC_MARK_DEBUG -extern FILE *js_DumpGCHeap; +extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap; #endif static JSBool @@ -198,14 +218,26 @@ GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSRuntime *rt; uint32 preBytes; - + rt = cx->runtime; preBytes = rt->gcBytes; #ifdef GC_MARK_DEBUG - js_DumpGCHeap = stdout; + if (argc && JSVAL_IS_STRING(argv[0])) { + char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + FILE *file = fopen(name, "w"); + if (!file) { + fprintf(stderr, "gc: can't open %s: %s\n", strerror(errno)); + return JS_FALSE; + } + js_DumpGCHeap = file; + } else { + js_DumpGCHeap = stdout; + } #endif js_ForceGC(cx); #ifdef GC_MARK_DEBUG + if (js_DumpGCHeap != stdout) + fclose(js_DumpGCHeap); js_DumpGCHeap = NULL; #endif printf("before %lu, after %lu, break %08lx\n", @@ -379,6 +411,7 @@ SingleNote(JSContext *cx, JSFunction *fun ) case SRC_LABELBRACE: case SRC_BREAK2LABEL: case SRC_CONT2LABEL: + case SRC_FUNCDEF: atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0); atom = js_GetAtom(cx, &fun->script->atomMap, atomIndex); printf(" atom %u (%s)", (uintN)atomIndex, ATOM_BYTES(atom)); @@ -406,6 +439,22 @@ Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_TRUE; } +static JSBool +ExceptionTable(JSContext *cx, JSFunction *fun) +{ + JSTryNote *iter = fun->script->trynotes; + + if (!iter) + 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++; + } + return JS_TRUE; +} + static JSBool Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -428,6 +477,7 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) js_Disassemble(cx, fun->script, lines, stdout); SingleNote(cx, fun); + ExceptionTable(cx, fun); } return JS_TRUE; } @@ -436,20 +486,19 @@ static JSBool DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #define LINE_BUF_LEN 512 - uintN i, k; + uintN i, len, line1, line2, bupline; JSFunction *fun; FILE *file; - char line_buf[LINE_BUF_LEN]; + char linebuf[LINE_BUF_LEN]; jsbytecode *pc, *end; - uintN len; - uintN lineno; + static char sep[] = ";-------------------------"; for (i = 0; i < argc; i++) { fun = JS_ValueToFunction(cx, argv[i]); if (!fun) return JS_FALSE; - if (! fun->script || ! fun->script->filename ) { + if (!fun->script || !fun->script->filename) { JS_ReportError(cx, "only works on JS scripts read from files"); return JS_FALSE; } @@ -464,16 +513,32 @@ DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval end = pc + fun->script->length; /* burn the leading lines */ - lineno = JS_PCToLineNumber(cx, fun->script, pc); - for (k = 0 ; k < lineno-1; k++) - fgets(line_buf, LINE_BUF_LEN, file); + line2 = JS_PCToLineNumber(cx, fun->script, pc); + for (line1 = 0; line1 < line2 - 1; line1++) + fgets(linebuf, LINE_BUF_LEN, file); + bupline = 0; while (pc < end) { - lineno = JS_PCToLineNumber(cx, fun->script, pc); + line2 = JS_PCToLineNumber(cx, fun->script, pc); - for ( ; k < lineno; k++ ) { - if (line_buf == fgets(line_buf, LINE_BUF_LEN, file)) - printf(";------------------------- %3u: %s", k+1, line_buf ); + if (line2 < line1) { + if (bupline != line2) { + bupline = line2; + printf("%s %3u: BACKUP\n", sep, line2); + } + } else { + if (bupline && line1 == line2) + printf("%s %3u: RESTORE\n", sep, line2); + bupline = 0; + while (line1 < line2) { + if (!fgets(linebuf, LINE_BUF_LEN, file)) { + JS_ReportError(cx, "unexpected EOF in %s", + fun->script->filename); + goto bail; + } + line1++; + printf("%s %3u: %s", sep, line1, linebuf); + } } len = js_Disassemble1(cx, fun->script, pc, pc - fun->script->code, @@ -482,6 +547,8 @@ DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval return JS_FALSE; pc += len; } + + bail: fclose(file); } return JS_TRUE; @@ -525,8 +592,8 @@ DumpAtom(PRHashEntry *he, int i, void *arg) FILE *fp = arg; JSAtom *atom = (JSAtom *)he; - fprintf(fp, "%3d %08x %5u %5lu ", - i, (uintN)he->keyHash, atom->index, (unsigned long)atom->number); + fprintf(fp, "%3d %08x %5lu ", + i, (uintN)he->keyHash, (unsigned long)atom->number); if (ATOM_IS_STRING(atom)) fprintf(fp, "\"%s\"\n", ATOM_BYTES(atom)); else if (ATOM_IS_INT(atom)) @@ -550,17 +617,19 @@ DumpSymbol(PRHashEntry *he, int i, void *arg) return HT_ENUMERATE_NEXT; } -extern JSScopeOps js_list_scope_ops; +extern JS_FRIEND_DATA(JSScopeOps) js_list_scope_ops; void -DumpScope(JSObject *obj, PRHashEnumerator dump, FILE *fp) +DumpScope(JSContext *cx, JSObject *obj, PRHashEnumerator dump, FILE *fp) { JSScope *scope; JSSymbol *sym; int i; - fprintf(fp, "\n%s scope contents:\n", obj->map->clasp->name); + fprintf(fp, "\n%s scope contents:\n", OBJ_GET_CLASS(cx, obj)->name); scope = (JSScope *)obj->map; + if (!MAP_IS_NATIVE(&scope->map)) + return; if (scope->ops == &js_list_scope_ops) { for (sym = (JSSymbol *)scope->data, i = 0; sym; sym = (JSSymbol *)sym->entry.next, i++) { @@ -582,8 +651,9 @@ DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSString *str; const char *bytes; JSAtom *atom; - JSBool ok; + JSObject *obj2; JSProperty *prop; + jsval value; for (i = 0; i < argc; i++) { str = JS_ValueToString(cx, argv[i]); @@ -598,29 +668,26 @@ DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) printf("\natom table contents:\n"); PR_HashTableDump(cx->runtime->atomState.table, DumpAtom, stdout); } else if (strcmp(bytes, "global") == 0) { - DumpScope(cx->globalObject, DumpSymbol, stdout); + DumpScope(cx, cx->globalObject, DumpSymbol, stdout); } else { - JS_LOCK(cx); atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0); - if (!atom) { - ok = JS_FALSE; - } else { - ok = js_FindProperty(cx, (jsval)atom, &obj, &prop); - js_DropAtom(cx, atom); - if (ok) { - if (!prop || - !JSVAL_IS_OBJECT(prop->object->slots[prop->slot])) { - fprintf(stderr, "js: invalid stats argument %s\n", - bytes); - } else { - obj = JSVAL_TO_OBJECT(prop->object->slots[prop->slot]); - DumpScope(obj, DumpSymbol, stdout); - } - } - } - JS_UNLOCK(cx); - if (!ok) + if (!atom) return JS_FALSE; + if (!js_FindProperty(cx, (jsid)atom, &obj, &obj2, &prop)) + return JS_FALSE; + if (prop) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &value)) + return JS_FALSE; + } + if (!prop || !JSVAL_IS_OBJECT(value)) { + fprintf(stderr, "js: invalid stats argument %s\n", + bytes); + continue; + } + obj = JSVAL_TO_OBJECT(value); + if (obj) + DumpScope(cx, obj, DumpSymbol, stdout); } } return JS_TRUE; @@ -633,8 +700,10 @@ static JSBool DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSAtom *atom; - jsval value; + JSObject *obj2; JSProperty *prop; + JSBool ok; + uintN attrs; if (argc != 2) { JS_ReportError(cx, "usage: doexp obj id"); @@ -646,10 +715,49 @@ DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) atom = js_ValueToStringAtom(cx, argv[1]); if (!atom) return JS_FALSE; - prop = js_GetProperty(cx, obj, (jsval)atom, &value); - if (!prop) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) return JS_FALSE; - prop->flags |= JSPROP_EXPORTED; + if (!prop) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + JSPROP_EXPORTED, NULL); + } else { + ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + return ok; +} +#endif + +#ifdef TEST_CVTARGS +static JSBool +ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool b; + jschar c; + int32 i, j; + uint32 u; + jsdouble d, I; + char *s; + JSString *str; + JSObject *obj; + JSFunction *fun; + jsval v; + + if (!JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSofv*", + &b, &c, &i, &u, &j, &d, &I, &s, &str, &obj, &fun, + &v)) { + return JS_FALSE; + } + printf("b %u, c %x (%c), i %ld, u %lu, j %ld\n", b, c, (char)c, i, u, j); + printf("d %g, I %g, s %s, S %s, obj %s, fun %s, v %s\n", + d, I, s, JS_GetStringBytes(str), + JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj))), + JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)), + JS_GetStringBytes(JS_ValueToString(cx, v))); return JS_TRUE; } #endif @@ -674,6 +782,9 @@ static JSFunctionSpec shell_functions[] = { #endif #ifdef TEST_EXPORT {"doexp", DoExport, 2}, +#endif +#ifdef TEST_CVTARGS + {"cvtargs", ConvertArgs, 0, 0, 12}, #endif {0} }; @@ -700,6 +811,9 @@ static char *shell_help_messages[] = { #endif #ifdef TEST_EXPORT "doexp obj id Export identified property from object", +#endif +#ifdef TEST_CVTARGS + "cvtargs b c ... Test JS_ConvertArguments", #endif 0 }; @@ -916,7 +1030,7 @@ my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) fputs("^\n", stderr); } -#ifdef XP_UNIX +#if defined DEBUG && defined XP_UNIX static JSBool Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -969,7 +1083,11 @@ Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool global_resolve(JSContext *cx, JSObject *obj, jsval id) { -#ifdef XP_UNIX +#if defined DEBUG && defined XP_UNIX + /* + * Do this expensive hack only for unoptimized Unix builds, which are not + * used for benchmarking. + */ char *path, *comp, *full; const char *name; JSBool ok, found; @@ -1054,30 +1172,60 @@ main(int argc, char **argv) argv++; #endif - rt = JS_Init(8L * 1024L * 1024L); + rt = JS_NewRuntime(8L * 1024L * 1024L); if (!rt) return 1; cx = JS_NewContext(rt, 8192); if (!cx) return 1; - if (version != JSVERSION_DEFAULT) - JS_SetVersion(cx, version); + JS_SetErrorReporter(cx, my_ErrorReporter); glob = JS_NewObject(cx, &global_class, NULL, NULL); if (!glob) return 1; if (!JS_InitStandardClasses(cx, glob)) return 1; - JS_SetErrorReporter(cx, my_ErrorReporter); if (!JS_DefineFunctions(cx, glob, shell_functions)) return 1; + /* Set version only after there is a global object. */ + if (version != JSVERSION_DEFAULT) + JS_SetVersion(cx, version); + it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); if (!it) return 1; if (!JS_DefineProperties(cx, it, its_props)) return 1; +#ifdef LIVECONNECT + if (!JSJ_SimpleInit(cx, glob, NULL, getenv("CLASSPATH"))) + return 1; +#endif + +#ifdef JSDEBUGGER + /* + * XXX A command line option to enable debugging (or not) would be good + */ + _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL); + if (!_jsdc) + return 1; + JSD_JSContextInUse(_jsdc, cx); + +#ifdef JSDEBUGGER_JAVA_UI + _jsdjc = JSDJ_CreateContext(); + if (! _jsdjc) + return 1; + JSDJ_SetJSDContext(_jsdjc, _jsdc); + JSDJ_CreateJavaVMAndStartDebugger(_jsdjc); + /* + * XXX This would be the place to wait for the debugger to start. + * Waiting would be nice in general, but especially when a js file + * is passed on the cmd line. + */ +#endif /* JSDEBUGGER_JAVA_UI */ +#endif /* JSDEBUGGER */ + if (argc > 0) { for (i = 0; i < argc; i++) Process(cx, glob, argv[i]); @@ -1085,7 +1233,13 @@ main(int argc, char **argv) Process(cx, glob, NULL); } +#ifdef JSDEBUGGER + if (_jsdc) + JSD_DebuggerOff(_jsdc); +#endif /* JSDEBUGGER */ + JS_DestroyContext(cx); - JS_Finish(rt); + JS_DestroyRuntime(rt); + JS_ShutDown(); return 0; } diff --git a/mozilla/js/src/js1640.def b/mozilla/js/src/js1640.def index 549e328630a..3ec677b2ab0 100644 --- a/mozilla/js/src/js1640.def +++ b/mozilla/js/src/js1640.def @@ -1,18 +1,3 @@ -; 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 -; compliance with the NPL. You may obtain a copy of the NPL at -; http://www.mozilla.org/NPL/ -; -; Software distributed under the NPL is distributed on an "AS IS" basis, -; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL -; for the specific language governing rights and limitations under the -; NPL. -; -; The Initial Developer of this code under the NPL is Netscape -; Communications Corporation. Portions created by Netscape are -; Copyright (C) 1998 Netscape Communications Corporation. All Rights -; Reserved. - LIBRARY JS1640.DLL EXETYPE WINDOWS PROTMODE diff --git a/mozilla/js/src/js1640.rc b/mozilla/js/src/js1640.rc index eac60a7f227..c14857e7a88 100644 --- a/mozilla/js/src/js1640.rc +++ b/mozilla/js/src/js1640.rc @@ -1,18 +1,3 @@ -// 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 -// compliance with the NPL. You may obtain a copy of the NPL at -// http://www.mozilla.org/NPL/ -// -// Software distributed under the NPL is distributed on an "AS IS" basis, -// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL -// for the specific language governing rights and limitations under the -// NPL. -// -// The Initial Developer of this code under the NPL is Netscape -// Communications Corporation. Portions created by Netscape are -// Copyright (C) 1998 Netscape Communications Corporation. All Rights -// Reserved. - ///////////////////////////////////////////////////////////////////////////// // Version stamp for this .DLL diff --git a/mozilla/js/src/js3240.rc b/mozilla/js/src/js3240.rc index e2846bf30cd..1a9f62c00a4 100644 --- a/mozilla/js/src/js3240.rc +++ b/mozilla/js/src/js3240.rc @@ -1,18 +1,3 @@ -// 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 -// compliance with the NPL. You may obtain a copy of the NPL at -// http://www.mozilla.org/NPL/ -// -// Software distributed under the NPL is distributed on an "AS IS" basis, -// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL -// for the specific language governing rights and limitations under the -// NPL. -// -// The Initial Developer of this code under the NPL is Netscape -// Communications Corporation. Portions created by Netscape are -// Copyright (C) 1998 Netscape Communications Corporation. All Rights -// Reserved. - //Microsoft Developer Studio generated resource script. // #include "resource.h" @@ -91,4 +76,4 @@ END #endif // APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/mozilla/js/src/jsaddr.c b/mozilla/js/src/jsaddr.c index c59d732ad0e..638dcb58de9 100644 --- a/mozilla/js/src/jsaddr.c +++ b/mozilla/js/src/jsaddr.c @@ -1,19 +1,5 @@ -/* -*- 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 - * compliance with the NPL. You may obtain a copy of the NPL at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the NPL is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL - * for the specific language governing rights and limitations under the - * NPL. - * - * The Initial Developer of this code under the NPL is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All Rights - * Reserved. +/* -*- Mode: C; tab-width: 8 -*- + * Copyright © 1996 Netscape Communications Corporation, All Rights Reserved. */ #include diff --git a/mozilla/js/src/jsaddr.h b/mozilla/js/src/jsaddr.h index ca853f7f159..d2ecde19a39 100644 --- a/mozilla/js/src/jsaddr.h +++ b/mozilla/js/src/jsaddr.h @@ -1,19 +1,5 @@ -/* -*- 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 - * compliance with the NPL. You may obtain a copy of the NPL at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the NPL is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL - * for the specific language governing rights and limitations under the - * NPL. - * - * The Initial Developer of this code under the NPL is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All Rights - * Reserved. +/* -*- Mode: C; tab-width: 8 -*- + * Copyright © 1996 Netscape Communications Corporation, All Rights Reserved. */ #ifndef jsaddr_h___ diff --git a/mozilla/js/src/jsapi.c b/mozilla/js/src/jsapi.c index a624c963cc6..3e61611a605 100644 --- a/mozilla/js/src/jsapi.c +++ b/mozilla/js/src/jsapi.c @@ -19,6 +19,7 @@ /* * JavaScript API. */ +#include #include #include #include @@ -34,7 +35,6 @@ #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" -#include "jsconfig.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdate.h" @@ -54,31 +54,150 @@ #include "jsscript.h" #include "jsstr.h" -PR_IMPLEMENT(jsval) +#if defined(JS_PARANOID_REQUEST) && defined(JS_THREADSAFE) +#define CHECK_REQUEST(cx) PR_ASSERT(cx->requestDepth) +#else +#define CHECK_REQUEST(cx) ((void)0) +#endif + +JS_PUBLIC_API(jsval) JS_GetNaNValue(JSContext *cx) { + CHECK_REQUEST(cx); return DOUBLE_TO_JSVAL(cx->runtime->jsNaN); } -PR_IMPLEMENT(jsval) +JS_PUBLIC_API(jsval) JS_GetNegativeInfinityValue(JSContext *cx) { + CHECK_REQUEST(cx); return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); } -PR_IMPLEMENT(jsval) +JS_PUBLIC_API(jsval) JS_GetPositiveInfinityValue(JSContext *cx) { + CHECK_REQUEST(cx); return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); } -PR_IMPLEMENT(jsval) +JS_PUBLIC_API(jsval) JS_GetEmptyStringValue(JSContext *cx) { + CHECK_REQUEST(cx); return STRING_TO_JSVAL(cx->runtime->emptyString); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) +JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, + ...) +{ + va_list ap; + uintN i; + JSBool required; + const char *cp; + JSFunction *fun; + jsdouble d; + JSString *str; + JSObject *obj; + + CHECK_REQUEST(cx); + va_start(ap, format); + i = 0; + required = JS_TRUE; + for (cp = format; *cp != '\0'; cp++) { + if (isspace(*cp)) + continue; + if (*cp == '/') { + required = JS_FALSE; + continue; + } + if (i == argc) { + if (required) { + fun = js_ValueToFunction(cx, &argv[-2], JS_FALSE); + if (fun) { + JS_ReportError(cx, + "%s requires more than %u argument%s", + JS_GetFunctionName(fun), + argc, (argc == 1) ? "" : "s"); + } + return JS_FALSE; + } + break; + } + switch (*cp) { + case 'b': + if (!js_ValueToBoolean(cx, argv[i], va_arg(ap, JSBool *))) + return JS_FALSE; + break; + case 'c': + if (!js_ValueToUint16(cx, argv[i], va_arg(ap, uint16 *))) + return JS_FALSE; + break; + case 'i': + if (!js_ValueToECMAInt32(cx, argv[i], va_arg(ap, int32 *))) + return JS_FALSE; + break; + case 'u': + if (!js_ValueToECMAUint32(cx, argv[i], va_arg(ap, uint32 *))) + return JS_FALSE; + break; + case 'j': + if (!js_ValueToInt32(cx, argv[i], va_arg(ap, int32 *))) + return JS_FALSE; + break; + case 'd': + if (!js_ValueToNumber(cx, argv[i], va_arg(ap, jsdouble *))) + return JS_FALSE; + break; + case 'I': + if (!js_ValueToNumber(cx, argv[i], &d)) + return JS_FALSE; + *va_arg(ap, jsdouble *) = js_DoubleToInteger(d); + break; + case 's': + str = js_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + argv[i] = STRING_TO_JSVAL(str); + *va_arg(ap, char **) = JS_GetStringBytes(str); + break; + case 'S': + str = js_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + argv[i] = STRING_TO_JSVAL(str); + *va_arg(ap, JSString **) = str; + break; + case 'o': + if (!js_ValueToObject(cx, argv[i], &obj)) + return JS_FALSE; + argv[i] = OBJECT_TO_JSVAL(obj); + *va_arg(ap, JSObject **) = obj; + break; + case 'f': + fun = js_ValueToFunction(cx, &argv[i], JS_FALSE); + if (!fun) + return JS_FALSE; + argv[i] = OBJECT_TO_JSVAL(fun->object); + *va_arg(ap, JSFunction **) = fun; + break; + case 'v': + *va_arg(ap, jsval *) = argv[i]; + break; + case '*': + break; + default: + JS_ReportError(cx, "invalid format character %c", *cp); + return JS_FALSE; + } + i++; + } + va_end(ap); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) { JSBool ok, b; @@ -87,7 +206,7 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) JSString *str; jsdouble d, *dp; - JS_LOCK(cx); + CHECK_REQUEST(cx); switch (type) { case JSTYPE_VOID: *vp = JSVAL_VOID; @@ -98,7 +217,7 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) *vp = OBJECT_TO_JSVAL(obj); break; case JSTYPE_FUNCTION: - fun = js_ValueToFunction(cx, v); + fun = js_ValueToFunction(cx, &v, JS_FALSE); ok = (fun != NULL); if (ok) *vp = OBJECT_TO_JSVAL(fun->object); @@ -128,66 +247,94 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) ok = JS_FALSE; break; } - JS_UNLOCK(cx); return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp) { - JS_LOCK_AND_RETURN_BOOL(cx, js_ValueToObject(cx, v, objp)); + CHECK_REQUEST(cx); + return js_ValueToObject(cx, v, objp); } -PR_IMPLEMENT(JSFunction *) +JS_PUBLIC_API(JSFunction *) JS_ValueToFunction(JSContext *cx, jsval v) { - JS_LOCK_AND_RETURN_TYPE(cx, JSFunction *, js_ValueToFunction(cx, v)); + CHECK_REQUEST(cx); + return js_ValueToFunction(cx, &v, JS_FALSE); } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSFunction *) +JS_ValueToConstructor(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); + return js_ValueToFunction(cx, &v, JS_TRUE); +} + +JS_PUBLIC_API(JSString *) JS_ValueToString(JSContext *cx, jsval v) { - JS_LOCK_AND_RETURN_STRING(cx, js_ValueToString(cx, v)); + CHECK_REQUEST(cx); + return js_ValueToString(cx, v); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) { - JS_LOCK_AND_RETURN_BOOL(cx, js_ValueToNumber(cx, v, dp)); + CHECK_REQUEST(cx); + return js_ValueToNumber(cx, v, dp); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) +JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToECMAInt32(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToECMAUint32(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip) { - JS_LOCK_AND_RETURN_BOOL(cx, js_ValueToInt32(cx, v, ip)); + CHECK_REQUEST(cx); + return js_ValueToInt32(cx, v, ip); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) { - JS_LOCK_AND_RETURN_BOOL(cx, js_ValueToUint16(cx, v, ip)); + CHECK_REQUEST(cx); + return js_ValueToUint16(cx, v, ip); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) { - JS_LOCK_AND_RETURN_BOOL(cx, js_ValueToBoolean(cx, v, bp)); + CHECK_REQUEST(cx); + return js_ValueToBoolean(cx, v, bp); } -PR_IMPLEMENT(JSType) +JS_PUBLIC_API(JSType) JS_TypeOfValue(JSContext *cx, jsval v) { JSType type; JSObject *obj; + CHECK_REQUEST(cx); if (JSVAL_IS_VOID(v)) { type = JSTYPE_VOID; } else if (JSVAL_IS_OBJECT(v)) { obj = JSVAL_TO_OBJECT(v); if (obj && - (obj->map->clasp == &js_FunctionClass + (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass #if JS_HAS_LEXICAL_CLOSURE - || obj->map->clasp == &js_ClosureClass + || OBJ_GET_CLASS(cx, obj) == &js_ClosureClass #endif )) { type = JSTYPE_FUNCTION; @@ -204,9 +351,10 @@ JS_TypeOfValue(JSContext *cx, jsval v) return type; } -PR_IMPLEMENT(const char *) +JS_PUBLIC_API(const char *) JS_GetTypeName(JSContext *cx, JSType type) { + CHECK_REQUEST(cx); if ((uintN)type >= (uintN)JSTYPE_LIMIT) return NULL; return js_type_str[type]; @@ -214,90 +362,204 @@ JS_GetTypeName(JSContext *cx, JSType type) /************************************************************************/ -PR_IMPLEMENT(JSRuntime *) -JS_Init(uint32 maxbytes) +JS_PUBLIC_API(JSRuntime *) +JS_NewRuntime(uint32 maxbytes) { JSRuntime *rt; + if (!js_InitStringGlobals()) + return NULL; rt = malloc(sizeof(JSRuntime)); if (!rt) return NULL; memset(rt, 0, sizeof(JSRuntime)); - if (!js_InitGC(rt, maxbytes)) { - free(rt); - return NULL; - } + if (!js_InitGC(rt, maxbytes)) + goto bad; +#ifdef JS_THREADSAFE + rt->gcLock = JS_NEW_LOCK(); + if (!rt->gcLock) + goto bad; + rt->gcDone = JS_NEW_CONDVAR(rt->gcLock); + if (!rt->gcDone) + goto bad; + rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); + if (!rt->requestDone) + goto bad; + js_SetupLocks(10); /* this is asymmetric with JS_ShutDown. */ + js_NewLock(&rt->rtLock); +#endif rt->propertyCache.empty = JS_TRUE; PR_INIT_CLIST(&rt->contextList); PR_INIT_CLIST(&rt->trapList); PR_INIT_CLIST(&rt->watchPointList); return rt; + +bad: + JS_DestroyRuntime(rt); + return NULL; } -PR_IMPLEMENT(void) -JS_Finish(JSRuntime *rt) +JS_PUBLIC_API(void) +JS_DestroyRuntime(JSRuntime *rt) { js_FinishGC(rt); +#ifdef JS_THREADSAFE + if (rt->gcLock) + JS_DESTROY_LOCK(rt->gcLock); + if (rt->gcDone) + JS_DESTROY_CONDVAR(rt->gcDone); + if (rt->requestDone) + JS_DESTROY_CONDVAR(rt->requestDone); + js_DestroyLock(&rt->rtLock); +#endif free(rt); } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) +JS_ShutDown(void) +{ + js_FreeStringGlobals(); +#ifdef JS_THREADSAFE + js_CleanupLocks(); +#endif +} + +#ifdef JS_THREADSAFE + +JS_PUBLIC_API(void) +JS_BeginRequest(JSContext *cx) +{ + JSRuntime *rt; + + if (!cx->requestDepth) { + /* Wait until the GC is finished. */ + rt = cx->runtime; + 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); + } + cx->requestDepth++; +} + +JS_PUBLIC_API(void) +JS_EndRequest(JSContext *cx) +{ + JSRuntime *rt; + + CHECK_REQUEST(cx); + cx->requestDepth--; + if (!cx->requestDepth) { + rt = cx->runtime; + JS_LOCK_GC(rt); + PR_ASSERT(rt->requestCount > 0); + rt->requestCount--; + JS_NOTIFY_REQUEST_DONE(rt); + JS_UNLOCK_GC(rt); + } +} + +/* Like JS_EndRequest, but don't notify any GC waiting in the wings. */ +JS_PUBLIC_API(void) +JS_SuspendRequest(JSContext *cx) +{ + JSRuntime *rt; + + CHECK_REQUEST(cx); + cx->requestDepth--; + if (!cx->requestDepth) { + rt = cx->runtime; + JS_LOCK_GC(rt); + PR_ASSERT(rt->requestCount > 0); + rt->requestCount--; + JS_UNLOCK_GC(rt); + } +} + +JS_PUBLIC_API(void) +JS_ResumeRequest(JSContext *cx) +{ + JSRuntime *rt; + + if (!cx->requestDepth) { + /* Wait until the GC is finished. */ + rt = cx->runtime; + 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); + } + cx->requestDepth++; +} + +#endif /* JS_THREADSAFE */ + +JS_PUBLIC_API(void) JS_Lock(JSRuntime *rt) { JS_LOCK_RUNTIME(rt); } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_Unlock(JSRuntime *rt) { JS_UNLOCK_RUNTIME(rt); } -PR_IMPLEMENT(JSContext *) +JS_PUBLIC_API(JSContext *) JS_NewContext(JSRuntime *rt, size_t stacksize) { - JSContext *cx; - - JS_LOCK_RUNTIME(rt); - cx = js_NewContext(rt, stacksize); - JS_UNLOCK_RUNTIME(rt); - return cx; + return js_NewContext(rt, stacksize); } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_DestroyContext(JSContext *cx) { - JS_LOCK_VOID(cx, js_DestroyContext(cx)); + js_DestroyContext(cx); } -PR_IMPLEMENT(JSRuntime *) +JS_PUBLIC_API(void*) +JS_GetContextPrivate(JSContext *cx) +{ + return cx->pvt; +} + +JS_PUBLIC_API(void) +JS_SetContextPrivate(JSContext *cx, void *pvt) +{ + cx->pvt = pvt; +} + +JS_PUBLIC_API(JSRuntime *) JS_GetRuntime(JSContext *cx) { return cx->runtime; } -PR_IMPLEMENT(JSContext *) +JS_PUBLIC_API(JSContext *) JS_ContextIterator(JSRuntime *rt, JSContext **iterp) { - JSContext *cx; - - JS_LOCK_RUNTIME(rt); - cx = js_ContextIterator(rt, iterp); - JS_UNLOCK_RUNTIME(rt); - return cx; + return js_ContextIterator(rt, iterp); } -PR_IMPLEMENT(JSVersion) +JS_PUBLIC_API(JSVersion) JS_GetVersion(JSContext *cx) { return cx->version; } -PR_IMPLEMENT(JSVersion) +JS_PUBLIC_API(JSVersion) JS_SetVersion(JSContext *cx, JSVersion version) { JSVersion oldVersion; + CHECK_REQUEST(cx); oldVersion = cx->version; cx->version = version; @@ -313,130 +575,114 @@ JS_SetVersion(JSContext *cx, JSVersion version) #if JS_HAS_EXPORT_IMPORT /* XXX this might fail due to low memory */ - JS_LOCK_VOID(cx, js_InitScanner(cx)); + js_InitScanner(cx); #endif /* JS_HAS_EXPORT_IMPORT */ return oldVersion; } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_GetGlobalObject(JSContext *cx) { return cx->globalObject; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_SetGlobalObject(JSContext *cx, JSObject *obj) { cx->globalObject = obj; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_InitStandardClasses(JSContext *cx, JSObject *obj) { - JSBool ok; - JSObject *proto, *fun_proto, *obj_proto, *array_proto; - - ok = JS_TRUE; - JS_LOCK(cx); + JSObject *fun_proto, *obj_proto, *array_proto; + CHECK_REQUEST(cx); /* If cx has no global object, use obj so prototypes can be found. */ if (!cx->globalObject) cx->globalObject = obj; +#if JS_HAS_UNDEFINED + /* Define a top-level property 'undefined' with the undefined value. + * (proposed ECMA v2.) + */ + if (!OBJ_DEFINE_PROPERTY(cx, obj, + (jsid)cx->runtime->atomState.typeAtoms[JSTYPE_VOID], + JSVAL_VOID, NULL, NULL, 0, NULL)) + return JS_FALSE; +#endif + /* Initialize the function class first so constructors can be made. */ fun_proto = js_InitFunctionClass(cx, obj); - if (!fun_proto) { - ok = JS_FALSE; - goto out; - } + if (!fun_proto) + return JS_FALSE; /* Initialize the object class next so Object.prototype works. */ obj_proto = js_InitObjectClass(cx, obj); - if (!obj_proto) { - ok = JS_FALSE; - goto out; - } + if (!obj_proto) + return JS_FALSE; - /* Link the global object and Function.prototype to Object.prototype. */ - proto = OBJ_GET_PROTO(obj); - if (!proto) - OBJ_SET_PROTO(obj, obj_proto); - proto = OBJ_GET_PROTO(fun_proto); - if (!proto) - OBJ_SET_PROTO(fun_proto, obj_proto); + /* Function.prototype and the global object delegate to Object.prototype. */ + OBJ_SET_PROTO(cx, fun_proto, obj_proto); + if (!OBJ_GET_PROTO(cx, obj)) + OBJ_SET_PROTO(cx, obj, obj_proto); /* Initialize the rest of the standard objects and functions. */ - ok = (array_proto = js_InitArrayClass(cx, obj)) && - js_InitCallAndClosureClasses(cx, obj, array_proto) && - js_InitBooleanClass(cx, obj) && - js_InitMathClass(cx, obj) && - js_InitNumberClass(cx, obj) && - js_InitStringClass(cx, obj) && + return (array_proto = js_InitArrayClass(cx, obj)) != NULL && + js_InitArgsCallClosureClasses(cx, obj, array_proto) && + js_InitBooleanClass(cx, obj) && + js_InitMathClass(cx, obj) && + js_InitNumberClass(cx, obj) && + js_InitStringClass(cx, obj) && #if JS_HAS_REGEXPS - js_InitRegExpClass(cx, obj) && + js_InitRegExpClass(cx, obj) && #endif - js_InitDateClass(cx, obj); - -out: - JS_UNLOCK(cx); - return ok; +#if JS_HAS_SCRIPT_OBJECT + js_InitScriptClass(cx, obj) && +#endif + js_InitDateClass(cx, obj); } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_GetScopeChain(JSContext *cx) { + CHECK_REQUEST(cx); return cx->fp ? cx->fp->scopeChain : NULL; } -PR_IMPLEMENT(void *) +JS_PUBLIC_API(void *) JS_malloc(JSContext *cx, size_t nbytes) { void *p; #ifdef XP_OS2 - if (nbytes == 0) /*DSR072897 - this sucks, but Microsoft allows it!*/ + if (nbytes == 0) /*DSR072897 - Windows allows this, OS/2 doesn't*/ nbytes = 1; #endif -#ifdef TOO_MUCH_GC - p = NULL; -#else p = malloc(nbytes); -#endif - if (!p) { - JS_LOCK_VOID(cx, js_GC(cx)); - p = malloc(nbytes); - if (!p) - JS_ReportOutOfMemory(cx); - } + if (!p) + JS_ReportOutOfMemory(cx); return p; } -PR_IMPLEMENT(void *) +JS_PUBLIC_API(void *) JS_realloc(JSContext *cx, void *p, size_t nbytes) { -#ifndef TOO_MUCH_GC p = realloc(p, nbytes); - if (!p) { -#endif - JS_LOCK_VOID(cx, js_GC(cx)); - p = realloc(p, nbytes); - if (!p) - JS_ReportOutOfMemory(cx); -#ifndef TOO_MUCH_GC - } -#endif + if (!p) + JS_ReportOutOfMemory(cx); return p; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_free(JSContext *cx, void *p) { if (p) free(p); } -PR_IMPLEMENT(char *) +JS_PUBLIC_API(char *) JS_strdup(JSContext *cx, const char *s) { char *p = JS_malloc(cx, strlen(s) + 1); @@ -445,40 +691,46 @@ JS_strdup(JSContext *cx, const char *s) return strcpy(p, s); } -PR_IMPLEMENT(jsdouble *) +JS_PUBLIC_API(jsdouble *) JS_NewDouble(JSContext *cx, jsdouble d) { - JS_LOCK_AND_RETURN_TYPE(cx, jsdouble *, js_NewDouble(cx, d)); + CHECK_REQUEST(cx); + return js_NewDouble(cx, d); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) { - JS_LOCK_AND_RETURN_BOOL(cx, js_NewDoubleValue(cx, d, rval)); + CHECK_REQUEST(cx); + return js_NewDoubleValue(cx, d, rval); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) { - JS_LOCK_AND_RETURN_BOOL(cx, js_NewNumberValue(cx, d, rval)); + CHECK_REQUEST(cx); + return js_NewNumberValue(cx, d, rval); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_AddRoot(JSContext *cx, void *rp) { - JS_LOCK_AND_RETURN_BOOL(cx, js_AddRoot(cx, rp)); + CHECK_REQUEST(cx); + return js_AddRoot(cx, rp, NULL); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_RemoveRoot(JSContext *cx, void *rp) { - JS_LOCK_AND_RETURN_BOOL(cx, js_RemoveRoot(cx, rp)); + CHECK_REQUEST(cx); + return js_RemoveRoot(cx, rp); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_AddNamedRoot(JSContext *cx, void *rp, const char *name) { - JS_LOCK_AND_RETURN_BOOL(cx, js_AddNamedRoot(cx, rp, name)); + CHECK_REQUEST(cx); + return js_AddRoot(cx, rp, name); } #ifdef DEBUG @@ -499,11 +751,12 @@ js_named_root_dumper(PRHashEntry *he, intN i, void *arg) { NamedRootDumpArgs *args = arg; - args->dump(he->value, (void *)he->key, args->data); + if (he->value) + args->dump(he->value, (void *)he->key, args->data); return HT_ENUMERATE_NEXT; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data) @@ -512,85 +765,128 @@ JS_DumpNamedRoots(JSRuntime *rt, args.dump = dump; args.data = data; - JS_LOCK_RUNTIME(rt); PR_HashTableEnumerateEntries(rt->gcRootsHash, js_named_root_dumper, &args); - JS_UNLOCK_RUNTIME(rt); } #endif /* DEBUG */ -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_LockGCThing(JSContext *cx, void *thing) { JSBool ok; - JS_LOCK_VOID(cx, ok = js_LockGCThing(cx, thing)); + CHECK_REQUEST(cx); + ok = js_LockGCThing(cx, thing); if (!ok) JS_ReportError(cx, "can't lock memory"); return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_UnlockGCThing(JSContext *cx, void *thing) { JSBool ok; - JS_LOCK_VOID(cx, ok = js_UnlockGCThing(cx, thing)); + CHECK_REQUEST(cx); + ok = js_UnlockGCThing(cx, thing); if (!ok) JS_ReportError(cx, "can't unlock memory"); return ok; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_GC(JSContext *cx) { - JS_LOCK(cx); if (!cx->fp) PR_FinishArenaPool(&cx->stackPool); PR_FinishArenaPool(&cx->codePool); PR_FinishArenaPool(&cx->tempPool); js_ForceGC(cx); - JS_UNLOCK(cx); } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_MaybeGC(JSContext *cx) { JSRuntime *rt; uint32 bytes, lastBytes; rt = cx->runtime; - JS_LOCK_RUNTIME(rt); bytes = rt->gcBytes; lastBytes = rt->gcLastBytes; if (bytes > 8192 && bytes > lastBytes + lastBytes / 2) js_GC(cx); - JS_UNLOCK_RUNTIME(rt); +} + +JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallback(JSContext *cx, JSGCCallback cb) +{ + JSRuntime *rt; + JSGCCallback oldcb; + + rt = cx->runtime; + oldcb = rt->gcCallback; + rt->gcCallback = cb; + return oldcb; } /************************************************************************/ -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(void) +JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) +{ + JS_free(cx, ida); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToId(JSContext *cx, jsval v, jsid *idp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + if (JSVAL_IS_INT(v)) { + *idp = v; + } else { + atom = js_ValueToStringAtom(cx, v); + if (!atom) + return JS_FALSE; + *idp = (jsid)atom; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_IdToValue(JSContext *cx, jsid id, jsval *vp) +{ + CHECK_REQUEST(cx); + *vp = js_IdToValue(id); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { + CHECK_REQUEST(cx); return JS_TRUE; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_EnumerateStub(JSContext *cx, JSObject *obj) { + CHECK_REQUEST(cx); return JS_TRUE; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id) { + CHECK_REQUEST(cx); return JS_TRUE; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { + CHECK_REQUEST(cx); #if JS_BUG_EAGER_TOSTRING if (type == JSTYPE_STRING) return JS_TRUE; @@ -599,241 +895,283 @@ JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) return JS_TRUE; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_FinalizeStub(JSContext *cx, JSObject *obj) { } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, JSClass *clasp, JSNative constructor, uintN nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs) { JSAtom *atom; - JSObject *proto, *fun_proto; + JSObject *proto, *ctor; + JSBool named; JSFunction *fun; jsval junk; - JS_LOCK(cx); + CHECK_REQUEST(cx); atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); - if (!atom) { - proto = NULL; - goto out; - } + if (!atom) + return NULL; - /* Define the constructor function in obj's scope. */ - fun = js_DefineFunction(cx, obj, atom, constructor, nargs, 0); - if (!fun) { - proto = NULL; - goto out; - } - - /* Construct a proto object for this class. */ - proto = js_NewObject(cx, clasp, parent_proto, fun->object); + /* Create a prototype object for this class. */ + proto = js_NewObject(cx, clasp, parent_proto, obj); if (!proto) - goto out; + return NULL; - /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ - if (fun->object->map->clasp == clasp) { - fun_proto = OBJ_GET_PROTO(fun->object); - if (!fun_proto) - OBJ_SET_PROTO(fun->object, proto); + if (!constructor) { + /* Lacking a constructor, name the prototype (e.g., Math). */ + named = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(proto), + NULL, NULL, 0, NULL); + if (!named) + goto bad; + ctor = proto; + } else { + /* Define the constructor function in obj's scope. */ + fun = js_DefineFunction(cx, obj, atom, constructor, nargs, 0); + named = (fun != NULL); + if (!fun) + goto bad; + + /* Connect constructor and prototype by named properties. */ + ctor = fun->object; + if (!js_SetClassPrototype(cx, ctor, proto, + JSPROP_READONLY | JSPROP_PERMANENT)) { + goto bad; + } + + /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ + if (OBJ_GET_CLASS(cx, ctor) == clasp) { + PR_ASSERT(!OBJ_GET_PROTO(cx, ctor)); + OBJ_SET_PROTO(cx, ctor, proto); + } } /* Add properties and methods to the prototype and the constructor. */ - if (!js_SetClassPrototype(cx, fun, proto) || - (ps && !JS_DefineProperties(cx, proto, ps)) || + if ((ps && !JS_DefineProperties(cx, proto, ps)) || (fs && !JS_DefineFunctions(cx, proto, fs)) || - (static_ps && !JS_DefineProperties(cx, fun->object, static_ps)) || - (static_fs && !JS_DefineFunctions(cx, fun->object, static_fs))) { - cx->newborn[GCX_OBJECT] = NULL; - proto = NULL; - goto out; + (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) || + (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) { + goto bad; } - -out: - if (!proto) - (void) js_DeleteProperty(cx, obj, (jsval)atom, &junk); - if (atom) - js_DropAtom(cx, atom); - JS_UNLOCK(cx); return proto; + +bad: + if (named) + (void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, &junk); + cx->newborn[GCX_OBJECT] = NULL; + return NULL; } -PR_IMPLEMENT(JSClass *) +#ifdef JS_THREADSAFE +JS_PUBLIC_API(JSClass *) +JS_GetClass(JSContext *cx, JSObject *obj) +{ + CHECK_REQUEST(cx); + return OBJ_GET_CLASS(cx, obj); +} +#else +JS_PUBLIC_API(JSClass *) JS_GetClass(JSObject *obj) { - return obj->map->clasp; + CHECK_REQUEST(cx); + return LOCKED_OBJ_GET_CLASS(obj); } +#endif -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) { JSFunction *fun; - if (obj->map->clasp == clasp) + CHECK_REQUEST(cx); + if (OBJ_GET_CLASS(cx, obj) == clasp) return JS_TRUE; if (argv) { - JS_LOCK_VOID(cx, fun = js_ValueToFunction(cx, argv[-2])); + fun = js_ValueToFunction(cx, &argv[-2], JS_FALSE); if (fun) { - JS_ReportError(cx, "method %s.%s called on incompatible %s", + JS_ReportError(cx, "%s.prototype.%s called on incompatible %s", clasp->name, JS_GetFunctionName(fun), - obj->map->clasp->name); + OBJ_GET_CLASS(cx, obj)->name); } } return JS_FALSE; } -PR_IMPLEMENT(void *) +JS_PUBLIC_API(void *) JS_GetPrivate(JSContext *cx, JSObject *obj) { jsval v; - PR_ASSERT(obj->map->clasp->flags & JSCLASS_HAS_PRIVATE); - JS_LOCK_VOID(cx, v = js_GetSlot(cx, obj, JSSLOT_PRIVATE)); + CHECK_REQUEST(cx); + PR_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_INT(v)) return NULL; return JSVAL_TO_PRIVATE(v); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) { - PR_ASSERT(obj->map->clasp->flags & JSCLASS_HAS_PRIVATE); - JS_LOCK_AND_RETURN_BOOL(cx, js_SetSlot(cx, obj, JSSLOT_PRIVATE, - PRIVATE_TO_JSVAL(data))); + CHECK_REQUEST(cx); + PR_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(data)); + return JS_TRUE; } -PR_IMPLEMENT(void *) +JS_PUBLIC_API(void *) JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) { + CHECK_REQUEST(cx); if (!JS_InstanceOf(cx, obj, clasp, argv)) return NULL; return JS_GetPrivate(cx, obj); } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_GetPrototype(JSContext *cx, JSObject *obj) { JSObject *proto; - JS_LOCK_VOID(cx, - proto = JSVAL_TO_OBJECT(js_GetSlot(cx, obj, JSSLOT_PROTO))); + CHECK_REQUEST(cx); + proto = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO)); /* Beware ref to dead object (we may be called from obj's finalizer). */ return proto && proto->map ? proto : NULL; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) { - JS_LOCK_AND_RETURN_BOOL(cx, js_SetSlot(cx, obj, JSSLOT_PROTO, - OBJECT_TO_JSVAL(proto))); + CHECK_REQUEST(cx); + OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)); + return JS_TRUE; } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_GetParent(JSContext *cx, JSObject *obj) { JSObject *parent; - JS_LOCK_VOID(cx, - parent = JSVAL_TO_OBJECT(js_GetSlot(cx, obj, JSSLOT_PARENT))); + CHECK_REQUEST(cx); + parent = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT)); /* Beware ref to dead object (we may be called from obj's finalizer). */ return parent && parent->map ? parent : NULL; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent) { - JS_LOCK_AND_RETURN_BOOL(cx, js_SetSlot(cx, obj, JSSLOT_PARENT, - OBJECT_TO_JSVAL(parent))); + CHECK_REQUEST(cx); + OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)); + return JS_TRUE; } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_GetConstructor(JSContext *cx, JSObject *proto) { - JSProperty *prop; + JSBool ok; jsval cval; - JS_LOCK(cx); - prop = js_GetProperty(cx, proto, - (jsval)cx->runtime->atomState.constructorAtom, + CHECK_REQUEST(cx); + ok = OBJ_GET_PROPERTY(cx, proto, + (jsid)cx->runtime->atomState.constructorAtom, &cval); - JS_UNLOCK(cx); - if (!prop) + if (!ok) return NULL; - if (!JSVAL_IS_FUNCTION(cval)) { - JS_ReportError(cx, "%s has no constructor", proto->map->clasp->name); + if (!JSVAL_IS_FUNCTION(cx, cval)) { + JS_ReportError(cx, "%s has no constructor", + OBJ_GET_CLASS(cx, proto)->name); return NULL; } return JSVAL_TO_OBJECT(cval); } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) { - JS_LOCK_AND_RETURN_TYPE(cx, JSObject *, - js_NewObject(cx, clasp, proto, parent)); + CHECK_REQUEST(cx); + return js_NewObject(cx, clasp, proto, parent); } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) { - JS_LOCK_AND_RETURN_TYPE(cx, JSObject *, - js_ConstructObject(cx, clasp, proto, parent)); + CHECK_REQUEST(cx); + return js_ConstructObject(cx, clasp, proto, parent); } -static JSProperty * +static JSBool DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN flags) + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp) { - jsval id; + jsid id; JSAtom *atom; - JSProperty *prop; - if (flags & JSPROP_INDEX) { + if (attrs & JSPROP_INDEX) { id = INT_TO_JSVAL((jsint)name); atom = NULL; } else { atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) - return NULL; - id = (jsval)atom; + return JS_FALSE; + id = (jsid)atom; } - prop = js_DefineProperty(cx, obj, id, value, getter, setter, flags); - if (atom) - js_DropAtom(cx, atom); - return prop; + return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs, + propp); } -PR_IMPLEMENT(JSObject *) +#define AUTO_NAMELEN(s,n) (((n) == (size_t)-1) ? js_strlen(s) : (n)) + +static JSBool +DefineUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, value, getter, setter, + attrs, propp); +} + +JS_PUBLIC_API(JSObject *) JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, - JSObject *proto, uintN flags) + JSObject *proto, uintN attrs) { JSObject *nobj; - JS_LOCK(cx); + CHECK_REQUEST(cx); nobj = js_NewObject(cx, clasp, proto, obj); - if (nobj && - !DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, flags)) { + if (!nobj) + return NULL; + if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs, + NULL)) { cx->newborn[GCX_OBJECT] = NULL; - nobj = NULL; + return NULL; } - JS_UNLOCK(cx); return nobj; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) { JSBool ok; jsval value; + uintN flags; - JS_LOCK(cx); + CHECK_REQUEST(cx); for (ok = JS_TRUE; cds->name; cds++) { #if PR_ALIGN_OF_DOUBLE == 8 /* @@ -851,346 +1189,605 @@ JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) if (!ok) break; #endif - ok = (DefineProperty(cx, obj, cds->name, value, - NULL, NULL, - /* XXX these should come from an API parameter */ - JSPROP_READONLY | JSPROP_PERMANENT) - != NULL); + flags = cds->flags; + if (!flags) + flags = JSPROP_READONLY | JSPROP_PERMANENT; + ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, flags, NULL); if (!ok) break; } - JS_UNLOCK(cx); return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps) { JSBool ok; JSProperty *prop; + JSScopeProperty *sprop; - JS_LOCK(cx); + CHECK_REQUEST(cx); for (ok = JS_TRUE; ps->name; ps++) { - prop = DefineProperty(cx, obj, ps->name, JSVAL_VOID, - ps->getter, ps->setter, ps->flags); - if (!prop) { - ok = JS_FALSE; + ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID, + ps->getter, ps->setter, ps->flags, + &prop); + if (!ok) break; + if (prop) { + if (OBJ_IS_NATIVE(obj)) { + sprop = (JSScopeProperty *)prop; + sprop->id = INT_TO_JSVAL(ps->tinyid); + sprop->attrs |= JSPROP_TINYIDHACK; + } + OBJ_DROP_PROPERTY(cx, obj, prop); } - prop->id = INT_TO_JSVAL(ps->tinyid); - prop->flags |= JSPROP_TINYIDHACK; } - JS_UNLOCK(cx); return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN flags) + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) { - JS_LOCK_AND_RETURN_BOOL(cx, DefineProperty(cx, obj, name, value, - getter, setter, flags) != NULL); + CHECK_REQUEST(cx); + return DefineProperty(cx, obj, name, value, getter, setter, attrs, NULL); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, int8 tinyid, jsval value, JSPropertyOp getter, JSPropertyOp setter, - uintN flags) + uintN attrs) { + JSBool ok; JSProperty *prop; + JSScopeProperty *sprop; - JS_LOCK(cx); - prop = DefineProperty(cx, obj, name, value, getter, setter, flags); - if (prop) { - prop->id = INT_TO_JSVAL(tinyid); - prop->flags |= JSPROP_TINYIDHACK; + CHECK_REQUEST(cx); + ok = DefineProperty(cx, obj, name, value, getter, setter, attrs, &prop); + if (ok && prop) { + if (OBJ_IS_NATIVE(obj)) { + sprop = (JSScopeProperty *)prop; + sprop->id = INT_TO_JSVAL(tinyid); + sprop->attrs |= JSPROP_TINYIDHACK; + } + OBJ_DROP_PROPERTY(cx, obj, prop); } - JS_UNLOCK(cx); - return prop != 0; + return ok; } static JSBool -LookupProperty(JSContext *cx, JSObject *obj, const char *name, +LookupProperty(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, JSProperty **propp) { JSAtom *atom; - JSBool ok; atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - ok = js_LookupProperty(cx, obj, (jsval)atom, NULL, propp); - js_DropAtom(cx, atom); - return ok; + return OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, objp, propp); } -PR_IMPLEMENT(JSBool) +static JSBool +LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSObject **objp, JSProperty **propp) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, objp, propp); +} + +JS_PUBLIC_API(JSBool) JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, const char *alias) { - JSBool ok; + JSObject *obj2; JSProperty *prop; - JSScope *scope; JSAtom *atom; + JSScope *scope; + JSBool ok; - JS_LOCK(cx); - ok = LookupProperty(cx, obj, name, &prop); - if (!ok) - goto out; + CHECK_REQUEST(cx); + /* XXXbe push this into jsobj.c or jsscope.c */ + if (!LookupProperty(cx, obj, name, &obj2, &prop)) + return JS_FALSE; if (!prop) { js_ReportIsNotDefined(cx, name); - ok = JS_FALSE; - goto out; + return JS_FALSE; } - scope = js_GetMutableScope(cx, obj); - if (!scope) { - ok = JS_FALSE; - goto out; + if (obj2 != obj || !OBJ_IS_NATIVE(obj2)) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + JS_ReportError(cx, "can't alias %s to %s in class %s", + alias, name, OBJ_GET_CLASS(cx, obj2)->name); + return JS_FALSE; } atom = js_Atomize(cx, alias, strlen(alias), 0); if (!atom) { ok = JS_FALSE; } else { - ok = (scope->ops->add(cx, scope, (jsval)atom, prop) != NULL); - js_DropAtom(cx, atom); + scope = (JSScope *) obj->map; + ok = (scope->ops->add(cx, scope, (jsid)atom, (JSScopeProperty *)prop) + != NULL); } -out: - JS_UNLOCK(cx); + OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } -PR_IMPLEMENT(JSBool) +static jsval +LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop) +{ + JSScopeProperty *sprop; + jsval rval; + + if (!prop) { + /* XXX bad API: no way to tell "not defined" from "void value" */ + return JSVAL_VOID; + } + if (OBJ_IS_NATIVE(obj2)) { + /* Peek at the native property's slot value, without doing a Get. */ + sprop = (JSScopeProperty *)prop; + rval = LOCKED_OBJ_GET_SLOT(obj2, sprop->slot); + } else { + /* XXX bad API: no way to return "defined but value unknown" */ + rval = JSVAL_TRUE; + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + return rval; +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN *attrsp, JSBool *foundp) +{ + JSAtom *atom; + JSObject *obj2; + JSProperty *prop; + JSBool ok; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + return JS_FALSE; + if (!prop || obj != obj2) { + *foundp = JS_FALSE; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_TRUE; + } + + *foundp = JS_TRUE; + ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, attrsp); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN attrs, JSBool *foundp) +{ + JSAtom *atom; + JSObject *obj2; + JSProperty *prop; + JSBool ok; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + return JS_FALSE; + if (!prop || obj != obj2) { + *foundp = JS_FALSE; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_TRUE; + } + + *foundp = JS_TRUE; + ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { JSBool ok; + JSObject *obj2; JSProperty *prop; - JS_LOCK(cx); - ok = LookupProperty(cx, obj, name, &prop); - if (ok) { - if (prop) - *vp = prop->object->slots[prop->slot]; - else - *vp = JSVAL_VOID; - } - JS_UNLOCK(cx); + CHECK_REQUEST(cx); + ok = LookupProperty(cx, obj, name, &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { JSAtom *atom; - JSBool ok; - JS_LOCK(cx); + CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) { - ok = JS_FALSE; - } else { - ok = (js_GetProperty(cx, obj, (jsval)atom, vp) != NULL); - js_DropAtom(cx, atom); - } - JS_UNLOCK(cx); - return ok; + if (!atom) + return JS_FALSE; + return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { JSAtom *atom; - JSBool ok; - JS_LOCK(cx); + CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) { - ok = JS_FALSE; - } else { - ok = (js_SetProperty(cx, obj, (jsval)atom, vp) != NULL); - js_DropAtom(cx, atom); - } - JS_UNLOCK(cx); - return ok; + if (!atom) + return JS_FALSE; + return OBJ_SET_PROPERTY(cx, obj, (jsid)atom, vp); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name) +{ + jsval junk; + + CHECK_REQUEST(cx); + return JS_DeleteProperty2(cx, obj, name, &junk); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, + jsval *rval) { JSAtom *atom; - JSBool ok; - jsval rval; /* XXX not in API */ - JS_LOCK(cx); + CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) { - ok = JS_FALSE; - } else { - ok = js_DeleteProperty(cx, obj, (jsval)atom, &rval); - js_DropAtom(cx, atom); - } - JS_UNLOCK(cx); - return ok; + if (!atom) + return JS_FALSE; + return OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, rval); } -PR_IMPLEMENT(JSObject *) -JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector) +JS_PUBLIC_API(JSBool) +JS_DefineUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs) { - JS_LOCK_AND_RETURN_TYPE(cx, JSObject *, - js_NewArrayObject(cx, length, vector)); + CHECK_REQUEST(cx); + return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, + attrs, NULL); } -PR_IMPLEMENT(JSBool) -JS_IsArrayObject(JSContext *cx, JSObject *obj) -{ - return obj->map->clasp == &js_ArrayClass; -} - -PR_IMPLEMENT(JSBool) -JS_GetArrayLength(JSContext *cx, JSObject *obj, jsint *lengthp) -{ - JS_LOCK_AND_RETURN_BOOL(cx, js_GetArrayLength(cx, obj, lengthp)); -} - -PR_IMPLEMENT(JSBool) -JS_SetArrayLength(JSContext *cx, JSObject *obj, jsint length) -{ - JS_LOCK_AND_RETURN_BOOL(cx, js_SetArrayLength(cx, obj, length)); -} - -PR_IMPLEMENT(JSBool) -JS_HasLengthProperty(JSContext *cx, JSObject *obj) -{ - JS_LOCK_AND_RETURN_BOOL(cx, js_HasLengthProperty(cx, obj) != NULL); -} - -PR_IMPLEMENT(JSBool) -JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN flags) -{ - JS_LOCK_AND_RETURN_BOOL(cx, js_DefineProperty(cx, obj, INT_TO_JSVAL(index), - value, getter, setter, flags) - != NULL); -} - -PR_IMPLEMENT(JSBool) -JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) +JS_PUBLIC_API(JSBool) +JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs) { JSBool ok; JSProperty *prop; - JSScope *scope; + JSScopeProperty *sprop; - JS_LOCK(cx); - ok = LookupProperty(cx, obj, name, &prop); - if (!ok) - goto out; - if (!prop) { - js_ReportIsNotDefined(cx, name); - ok = JS_FALSE; - goto out; + CHECK_REQUEST(cx); + ok = DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, + &prop); + if (ok && prop) { + if (OBJ_IS_NATIVE(obj)) { + sprop = (JSScopeProperty *)prop; + sprop->id = INT_TO_JSVAL(tinyid); + sprop->attrs |= JSPROP_TINYIDHACK; + } + OBJ_DROP_PROPERTY(cx, obj, prop); } - scope = js_GetMutableScope(cx, obj); - if (!scope) - ok = JS_FALSE; - else - ok = (scope->ops->add(cx, scope, INT_TO_JSVAL(alias), prop) != NULL); -out: - JS_UNLOCK(cx); return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) +JS_LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_GetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); +} + +JS_PUBLIC_API(JSBool) +JS_SetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_SET_PROPERTY(cx, obj, (jsid)atom, vp); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *rval) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, rval); +} + +JS_PUBLIC_API(JSObject *) +JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector) +{ + CHECK_REQUEST(cx); + /* jsuint cast does ToUint32 */ + return js_NewArrayObject(cx, (jsuint)length, vector); +} + +JS_PUBLIC_API(JSBool) +JS_IsArrayObject(JSContext *cx, JSObject *obj) +{ + CHECK_REQUEST(cx); + return OBJ_GET_CLASS(cx, obj) == &js_ArrayClass; +} + +JS_PUBLIC_API(JSBool) +JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + CHECK_REQUEST(cx); + return js_GetLengthProperty(cx, obj, lengthp); +} + +JS_PUBLIC_API(JSBool) +JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length) +{ + CHECK_REQUEST(cx); + return js_SetLengthProperty(cx, obj, length); +} + +JS_PUBLIC_API(JSBool) +JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + CHECK_REQUEST(cx); + return js_HasLengthProperty(cx, obj, lengthp); +} + +JS_PUBLIC_API(JSBool) +JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) +{ + CHECK_REQUEST(cx); + return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(index), value, + getter, setter, attrs, NULL); +} + +JS_PUBLIC_API(JSBool) +JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) +{ + JSObject *obj2; + JSProperty *prop; + JSScope *scope; + JSBool ok; + + CHECK_REQUEST(cx); + /* XXXbe push this into jsobj.c or jsscope.c */ + if (!LookupProperty(cx, obj, name, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + js_ReportIsNotDefined(cx, name); + return JS_FALSE; + } + if (obj2 != obj || !OBJ_IS_NATIVE(obj2)) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + JS_ReportError(cx, "can't alias %ld to %s in class %s", + (long)alias, name, OBJ_GET_CLASS(cx, obj2)->name); + return JS_FALSE; + } + scope = (JSScope *) obj->map; + ok = (scope->ops->add(cx, scope, INT_TO_JSVAL(alias), + (JSScopeProperty *)prop) + != NULL); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) { JSBool ok; + JSObject *obj2; JSProperty *prop; - JS_LOCK(cx); - ok = js_LookupProperty(cx, obj, INT_TO_JSVAL(index), NULL, &prop); - if (ok) { - if (prop) - *vp = prop->object->slots[prop->slot]; - else - *vp = JSVAL_VOID; - } - JS_UNLOCK(cx); + CHECK_REQUEST(cx); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSVAL(index), &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) { - JS_LOCK_AND_RETURN_BOOL(cx, - js_GetProperty(cx, obj, INT_TO_JSVAL(index), vp) - != NULL); + CHECK_REQUEST(cx); + return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSVAL(index), vp); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) { - JS_LOCK_AND_RETURN_BOOL(cx, - js_SetProperty(cx, obj, INT_TO_JSVAL(index), vp) - != NULL); + CHECK_REQUEST(cx); + return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSVAL(index), vp); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index) { - jsval rval; /* XXX not in API */ + jsval junk; - JS_LOCK_AND_RETURN_BOOL(cx, - js_DeleteProperty(cx, obj, INT_TO_JSVAL(index), - &rval)); + CHECK_REQUEST(cx); + return JS_DeleteElement2(cx, obj, index, &junk); } -PR_IMPLEMENT(void) +JS_PUBLIC_API(JSBool) +JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval) +{ + CHECK_REQUEST(cx); + return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSVAL(index), rval); +} + +JS_PUBLIC_API(void) JS_ClearScope(JSContext *cx, JSObject *obj) { + JSObjectMap *map; JSScope *scope; - JS_LOCK(cx); - - /* Avoid lots of js_FlushPropertyCacheByProp activity. */ - js_FlushPropertyCache(cx); - - scope = (JSScope *)obj->map; - scope->ops->clear(cx, scope); + CHECK_REQUEST(cx); + /* XXXbe push this into jsobj.c or jsscope.c */ + JS_LOCK_OBJ(cx, obj); + map = obj->map; + if (MAP_IS_NATIVE(map)) { + scope = (JSScope *)map; + scope->ops->clear(cx, scope); + } /* Reset freeslot so we're consistent. */ - if (scope->map.clasp->flags & JSCLASS_HAS_PRIVATE) - scope->map.freeslot = JSSLOT_PRIVATE + 1; - else - scope->map.freeslot = JSSLOT_START; - - JS_UNLOCK(cx); + map->freeslot = JSSLOT_FREE(OBJ_GET_CLASS(cx, obj)); + JS_UNLOCK_OBJ(cx, obj); } -PR_IMPLEMENT(JSFunction *) +JS_PUBLIC_API(JSIdArray *) +JS_Enumerate(JSContext *cx, JSObject *obj) +{ + jsint i, n; + jsval iter_state, num_properties; + jsid id; + JSIdArray *ida; + jsval *vector; + + CHECK_REQUEST(cx); + + ida = NULL; + iter_state = JSVAL_NULL; + + /* Get the number of properties to enumerate. */ + if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties)) + goto error; + if (!JSVAL_IS_INT(num_properties)) { + PR_ASSERT(0); + goto error; + } + + n = JSVAL_TO_INT(num_properties); + + /* Create an array of jsids large enough to hold all the properties */ + ida = js_NewIdArray(cx, n); + if (n) { + i = 0; + vector = &ida->vector[0]; + while (1) { + if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &id)) + goto error; + + /* No more jsid's to enumerate ? */ + if (!iter_state) + break; + vector[i++] = id; + } + } + + return ida; + +error: + if (iter_state != JSVAL_NULL) + OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); + if (ida) + JS_DestroyIdArray(cx, ida); + return NULL; +} + +JS_PUBLIC_API(JSBool) +JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + CHECK_REQUEST(cx); + return OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, attrsp); +} + +JS_PUBLIC_API(JSFunction *) JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, JSObject *parent, const char *name) { JSAtom *atom; - JSFunction *fun; - JS_LOCK(cx); + CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) { - fun = NULL; - } else { - fun = js_NewFunction(cx, call, nargs, flags, parent, atom); - js_DropAtom(cx, atom); - } - JS_UNLOCK(cx); - return fun; + if (!atom) + return NULL; + return js_NewFunction(cx, NULL, call, nargs, flags, parent, atom); } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) +JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) +{ + JSFunction *fun; + JSObject *newfunobj; + + CHECK_REQUEST(cx); + if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) + return NULL; + fun = JS_GetPrivate(cx, funobj); + + newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent); + if (!newfunobj) + return NULL; + if (!js_LinkFunctionObject(cx, fun, newfunobj)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + + return newfunobj; +} + +JS_PUBLIC_API(JSObject *) JS_GetFunctionObject(JSFunction *fun) { return fun->object; } -PR_IMPLEMENT(const char *) +JS_PUBLIC_API(const char *) JS_GetFunctionName(JSFunction *fun) { return fun->atom @@ -1198,68 +1795,68 @@ JS_GetFunctionName(JSFunction *fun) : js_anonymous_str; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) { JSFunction *fun; + CHECK_REQUEST(cx); for (; fs->name; fs++) { fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, fs->flags); if (!fun) return JS_FALSE; + fun->extra = fs->extra; } return JS_TRUE; } -PR_IMPLEMENT(JSFunction *) +JS_PUBLIC_API(JSFunction *) JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, - uintN nargs, uintN flags) + uintN nargs, uintN attrs) { JSAtom *atom; - JSFunction *fun; - JS_LOCK(cx); + CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) { - fun = NULL; - } else { - fun = js_DefineFunction(cx, obj, atom, call, nargs, flags); - js_DropAtom(cx, atom); - } - JS_UNLOCK(cx); - return fun; + if (!atom) + return NULL; + return js_DefineFunction(cx, obj, atom, call, nargs, attrs); } static JSScript * CompileTokenStream(JSContext *cx, JSObject *obj, JSTokenStream *ts, void *tempMark) { - void *codeMark; JSCodeGenerator cg; - uintN lineno; JSScript *script; + uintN lineno; - PR_ASSERT(JS_IS_LOCKED(cx)); - codeMark = PR_ARENA_MARK(&cx->codePool); - if (!js_InitCodeGenerator(cx, &cg, &cx->codePool)) - return NULL; - lineno = ts->lineno; - if (js_Parse(cx, obj, ts, &cg)) - script = js_NewScript(cx, &cg, ts->filename, lineno, - ts->principals, NULL); - else + CHECK_REQUEST(cx); + if (!js_InitCodeGenerator(cx, &cg, ts->filename, ts->lineno, + ts->principals)) { script = NULL; - if (!js_CloseTokenStream(cx, ts) && script) { + goto out; + } + lineno = ts->lineno; + if (!js_CompileTokenStream(cx, obj, ts, &cg)) { + script = NULL; + goto out; + } + script = js_NewScriptFromCG(cx, &cg, NULL); + if (!script) + goto out; + if (!js_CloseTokenStream(cx, ts)) { js_DestroyScript(cx, script); script = NULL; } - PR_ARENA_RELEASE(&cx->codePool, codeMark); - PR_ARENA_RELEASE(&cx->tempPool, tempMark); +out: + cg.tempMark = tempMark; + js_ResetCodeGenerator(cx, &cg); return script; } -PR_IMPLEMENT(JSScript *) +JS_PUBLIC_API(JSScript *) JS_CompileScript(JSContext *cx, JSObject *obj, const char *bytes, size_t length, const char *filename, uintN lineno) @@ -1267,6 +1864,7 @@ JS_CompileScript(JSContext *cx, JSObject *obj, jschar *chars; JSScript *script; + CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, length); if (!chars) return NULL; @@ -1275,7 +1873,7 @@ JS_CompileScript(JSContext *cx, JSObject *obj, return script; } -PR_IMPLEMENT(JSScript *) +JS_PUBLIC_API(JSScript *) JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, size_t length, @@ -1284,6 +1882,7 @@ JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, jschar *chars; JSScript *script; + CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, length); if (!chars) return NULL; @@ -1293,16 +1892,17 @@ JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, return script; } -PR_IMPLEMENT(JSScript *) +JS_PUBLIC_API(JSScript *) JS_CompileUCScript(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno) { + CHECK_REQUEST(cx); return JS_CompileUCScriptForPrincipals(cx, obj, NULL, chars, length, filename, lineno); } -PR_IMPLEMENT(JSScript *) +JS_PUBLIC_API(JSScript *) JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, size_t length, @@ -1310,57 +1910,53 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, { void *mark; JSTokenStream *ts; - JSScript *script; - JS_LOCK(cx); + CHECK_REQUEST(cx); mark = PR_ARENA_MARK(&cx->tempPool); ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); - if (ts) - script = CompileTokenStream(cx, obj, ts, mark); - else - script = NULL; - JS_UNLOCK(cx); - return script; + if (!ts) + return NULL; + return CompileTokenStream(cx, obj, ts, mark); } #ifdef JSFILE -PR_IMPLEMENT(JSScript *) +JS_PUBLIC_API(JSScript *) JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) { void *mark; JSTokenStream *ts; - JSScript *script; - JS_LOCK(cx); + CHECK_REQUEST(cx); mark = PR_ARENA_MARK(&cx->tempPool); - if (filename && strcmp(filename, "-") != 0) { - ts = js_NewFileTokenStream(cx, filename); - if (!ts) { - script = NULL; - goto out; - } - } else { - ts = js_NewBufferTokenStream(cx, NULL, 0); - if (!ts) { - script = NULL; - goto out; - } - ts->file = stdin; - } - script = CompileTokenStream(cx, obj, ts, mark); -out: - JS_UNLOCK(cx); - return script; + ts = js_NewFileTokenStream(cx, filename, stdin); + if (!ts) + return NULL; + return CompileTokenStream(cx, obj, ts, mark); } #endif -PR_IMPLEMENT(void) -JS_DestroyScript(JSContext *cx, JSScript *script) +JS_PUBLIC_API(JSObject *) +JS_NewScriptObject(JSContext *cx, JSScript *script) { - JS_LOCK_VOID(cx, js_DestroyScript(cx, script)); + JSObject *obj; + + CHECK_REQUEST(cx); + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return NULL; + JS_SetPrivate(cx, obj, script); + script->object = obj; + return obj; } -PR_IMPLEMENT(JSFunction *) +JS_PUBLIC_API(void) +JS_DestroyScript(JSContext *cx, JSScript *script) +{ + CHECK_REQUEST(cx); + js_DestroyScript(cx, script); +} + +JS_PUBLIC_API(JSFunction *) JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, uintN nargs, const char **argnames, const char *bytes, size_t length, @@ -1369,6 +1965,7 @@ JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, jschar *chars; JSFunction *fun; + CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, length); if (!chars) return NULL; @@ -1378,7 +1975,7 @@ JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, return fun; } -PR_IMPLEMENT(JSFunction *) +JS_PUBLIC_API(JSFunction *) JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *name, uintN nargs, const char **argnames, @@ -1388,6 +1985,7 @@ JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, jschar *chars; JSFunction *fun; + CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, length); if (!chars) return NULL; @@ -1398,19 +1996,20 @@ JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, return fun; } -PR_IMPLEMENT(JSFunction *) +JS_PUBLIC_API(JSFunction *) JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, uintN nargs, const char **argnames, const jschar *chars, size_t length, const char *filename, uintN lineno) { + CHECK_REQUEST(cx); return JS_CompileUCFunctionForPrincipals(cx, obj, NULL, name, nargs, argnames, chars, length, filename, lineno); } -PR_IMPLEMENT(JSFunction *) +JS_PUBLIC_API(JSFunction *) JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *name, uintN nargs, const char **argnames, @@ -1421,12 +2020,11 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, JSTokenStream *ts; JSFunction *fun; JSAtom *funAtom, *argAtom; - JSSymbol *arg, *args, **argp; uintN i; - JSProperty *prop; + JSScopeProperty *sprop; jsval junk; - JS_LOCK(cx); + CHECK_REQUEST(cx); mark = PR_ARENA_MARK(&cx->tempPool); ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); if (!ts) { @@ -1442,100 +2040,90 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, fun = js_DefineFunction(cx, obj, funAtom, NULL, nargs, 0); if (!fun) goto out; - args = NULL; if (nargs) { - argp = &args; for (i = 0; i < nargs; i++) { argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); if (!argAtom) break; - prop = js_DefineProperty(cx, fun->object, (jsval)argAtom, - JSVAL_VOID, js_GetArgument, js_SetArgument, - JSPROP_ENUMERATE|JSPROP_PERMANENT); - js_DropAtom(cx, argAtom); - if (!prop) + if (!js_DefineProperty(cx, fun->object, (jsid)argAtom, + JSVAL_VOID, js_GetArgument, js_SetArgument, + JSPROP_ENUMERATE|JSPROP_PERMANENT, + (JSProperty **)&sprop)) { break; - prop->id = INT_TO_JSVAL(i); - arg = prop->symbols; - *argp = arg; - argp = &arg->next; + } + PR_ASSERT(sprop); + sprop->id = INT_TO_JSVAL(i); + OBJ_DROP_PROPERTY(cx, fun->object, (JSProperty *)sprop); } if (i < nargs) { - (void) js_DeleteProperty(cx, obj, (jsval)funAtom, &junk); + (void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)funAtom, &junk); fun = NULL; goto out; } } - if (!js_ParseFunctionBody(cx, ts, fun, args)) { - (void) js_DeleteProperty(cx, obj, (jsval)funAtom, &junk); + if (!js_CompileFunctionBody(cx, ts, fun)) { + (void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)funAtom, &junk); fun = NULL; } out: - if (funAtom) - js_DropAtom(cx, funAtom); if (ts) js_CloseTokenStream(cx, ts); PR_ARENA_RELEASE(&cx->tempPool, mark); - JS_UNLOCK(cx); return fun; } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN indent) { JSPrinter *jp; JSString *str; - JS_LOCK(cx); + CHECK_REQUEST(cx); jp = js_NewPrinter(cx, name, indent); - if (!jp) { + if (!jp) + return NULL; + if (js_DecompileScript(jp, script)) + str = js_GetPrinterOutput(jp); + else str = NULL; - } else { - if (js_DecompileScript(jp, script)) - str = js_GetPrinterOutput(jp); - else - str = NULL; - js_DestroyPrinter(jp); - } - JS_UNLOCK(cx); + js_DestroyPrinter(jp); return str; } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent) { JSPrinter *jp; JSString *str; - JS_LOCK(cx); + CHECK_REQUEST(cx); jp = js_NewPrinter(cx, JS_GetFunctionName(fun), indent); - if (!jp) { + if (!jp) + return NULL; + if (js_DecompileFunction(jp, fun, JS_TRUE)) + str = js_GetPrinterOutput(jp); + else str = NULL; - } else { - if (js_DecompileFunction(jp, fun, JS_TRUE)) - str = js_GetPrinterOutput(jp); - else - str = NULL; - js_DestroyPrinter(jp); - } - JS_UNLOCK(cx); + js_DestroyPrinter(jp); return str; } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) { + CHECK_REQUEST(cx); return JS_DecompileScript(cx, fun->script, JS_GetFunctionName(fun), indent); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) { - return js_Execute(cx, obj, script, NULL, rval); + CHECK_REQUEST(cx); + return js_Execute(cx, obj, script, NULL, NULL, JS_FALSE, rval); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_EvaluateScript(JSContext *cx, JSObject *obj, const char *bytes, uintN length, const char *filename, uintN lineno, @@ -1544,6 +2132,7 @@ JS_EvaluateScript(JSContext *cx, JSObject *obj, jschar *chars; JSBool ok; + CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, length); if (!chars) return JS_FALSE; @@ -1552,7 +2141,7 @@ JS_EvaluateScript(JSContext *cx, JSObject *obj, return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, uintN length, @@ -1562,6 +2151,7 @@ JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, jschar *chars; JSBool ok; + CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, length); if (!chars) return JS_FALSE; @@ -1571,17 +2161,18 @@ JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_EvaluateUCScript(JSContext *cx, JSObject *obj, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval) { + CHECK_REQUEST(cx); return JS_EvaluateUCScriptForPrincipals(cx, obj, NULL, chars, length, filename, lineno, rval); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, uintN length, @@ -1591,66 +2182,78 @@ JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSScript *script; JSBool ok; + CHECK_REQUEST(cx); script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, filename, lineno); if (!script) return JS_FALSE; - ok = js_Execute(cx, obj, script, NULL, rval); + ok = js_Execute(cx, obj, script, NULL, NULL, JS_FALSE, rval); JS_DestroyScript(cx, script); return ok; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval *argv, jsval *rval) { - JS_LOCK_AND_RETURN_BOOL(cx, - js_Call(cx, obj, OBJECT_TO_JSVAL(fun->object), - argc, argv, rval)); + CHECK_REQUEST(cx); + return js_CallFunctionValue(cx, obj, OBJECT_TO_JSVAL(fun->object), + argc, argv, rval); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *argv, jsval *rval) { jsval fval; + CHECK_REQUEST(cx); if (!JS_GetProperty(cx, obj, name, &fval)) return JS_FALSE; - JS_LOCK_AND_RETURN_BOOL(cx, js_Call(cx, obj, fval, argc, argv, rval)); + return js_CallFunctionValue(cx, obj, fval, argc, argv, rval); } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, jsval *rval) { - JS_LOCK_AND_RETURN_BOOL(cx, js_Call(cx, obj, fval, argc, argv, rval)); + CHECK_REQUEST(cx); + return js_CallFunctionValue(cx, obj, fval, argc, argv, rval); } -PR_IMPLEMENT(JSBranchCallback) +JS_PUBLIC_API(JSBranchCallback) JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb) { JSBranchCallback oldcb; + CHECK_REQUEST(cx); oldcb = cx->branchCallback; cx->branchCallback = cb; return oldcb; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx) { return cx->fp != NULL; } +JS_PUBLIC_API(JSBool) +JS_IsConstructing(JSContext *cx) +{ + CHECK_REQUEST(cx); + return cx->fp && cx->fp->constructing; +} + /************************************************************************/ -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_NewString(JSContext *cx, char *bytes, size_t length) { jschar *chars; JSString *str; + CHECK_REQUEST(cx); /* Make a Unicode vector from the 8-bit char codes in bytes. */ chars = js_InflateString(cx, bytes, length); if (!chars) @@ -1669,12 +2272,13 @@ JS_NewString(JSContext *cx, char *bytes, size_t length) return str; } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) { jschar *js; JSString *str; + CHECK_REQUEST(cx); js = js_InflateString(cx, s, n); if (!js) return NULL; @@ -1684,13 +2288,14 @@ JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) return str; } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_NewStringCopyZ(JSContext *cx, const char *s) { size_t n; jschar *js; JSString *str; + CHECK_REQUEST(cx); if (!s) return cx->runtime->emptyString; n = strlen(s); @@ -1703,68 +2308,79 @@ JS_NewStringCopyZ(JSContext *cx, const char *s) return str; } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_InternString(JSContext *cx, const char *s) { JSAtom *atom; - JS_LOCK_VOID(cx, atom = js_Atomize(cx, s, strlen(s), 0)); + CHECK_REQUEST(cx); + atom = js_Atomize(cx, s, strlen(s), ATOM_PINNED); if (!atom) return NULL; return ATOM_TO_STRING(atom); } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_NewUCString(JSContext *cx, jschar *chars, size_t length) { + CHECK_REQUEST(cx); return js_NewString(cx, chars, length, 0); } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n) { + CHECK_REQUEST(cx); return js_NewStringCopyN(cx, s, n, 0); } -PR_IMPLEMENT(JSString *) +JS_PUBLIC_API(JSString *) JS_NewUCStringCopyZ(JSContext *cx, const jschar *s) { + CHECK_REQUEST(cx); if (!s) return cx->runtime->emptyString; return js_NewStringCopyZ(cx, s, 0); } -PR_IMPLEMENT(JSString *) -JS_InternUCString(JSContext *cx, const jschar *s) +JS_PUBLIC_API(JSString *) +JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length) { JSAtom *atom; - JS_LOCK_VOID(cx, atom = js_AtomizeChars(cx, s, js_strlen(s), 0)); + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, s, length, ATOM_PINNED); if (!atom) return NULL; return ATOM_TO_STRING(atom); } -PR_IMPLEMENT(char *) +JS_PUBLIC_API(JSString *) +JS_InternUCString(JSContext *cx, const jschar *s) +{ + return JS_InternUCStringN(cx, s, js_strlen(s)); +} + +JS_PUBLIC_API(char *) JS_GetStringBytes(JSString *str) { char *bytes = js_GetStringBytes(str); return bytes ? bytes : ""; } -PR_IMPLEMENT(jschar *) +JS_PUBLIC_API(jschar *) JS_GetStringChars(JSString *str) { return str->chars; } -PR_IMPLEMENT(size_t) +JS_PUBLIC_API(size_t) JS_GetStringLength(JSString *str) { return str->length; } -PR_IMPLEMENT(intN) +JS_PUBLIC_API(intN) JS_CompareStrings(JSString *str1, JSString *str2) { return js_CompareStrings(str1, str2); @@ -1772,27 +2388,29 @@ JS_CompareStrings(JSString *str1, JSString *str2) /************************************************************************/ -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ReportError(JSContext *cx, const char *format, ...) { va_list ap; + CHECK_REQUEST(cx); va_start(ap, format); js_ReportErrorVA(cx, format, ap); va_end(ap); } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ReportOutOfMemory(JSContext *cx) { JS_ReportError(cx, "out of memory"); } -PR_IMPLEMENT(JSErrorReporter) +JS_PUBLIC_API(JSErrorReporter) JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) { JSErrorReporter older; + CHECK_REQUEST(cx); older = cx->errorReporter; cx->errorReporter = er; return older; @@ -1803,53 +2421,56 @@ JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) /* * Regular Expressions. */ -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) { #if JS_HAS_REGEXPS jschar *chars; JSObject *obj; + CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, length); if (!chars) return NULL; - JS_LOCK_VOID(cx, obj = js_NewRegExpObject(cx, chars, length, flags)); + obj = js_NewRegExpObject(cx, chars, length, flags); JS_free(cx, chars); return obj; #else JS_ReportError(cx, "sorry, regular expression are not supported"); - return JS_FALSE; + return NULL; #endif } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags) { + CHECK_REQUEST(cx); #if JS_HAS_REGEXPS - JS_LOCK_AND_RETURN_TYPE(cx, JSObject *, - js_NewRegExpObject(cx, chars, length, flags)); + return js_NewRegExpObject(cx, chars, length, flags); #else JS_ReportError(cx, "sorry, regular expression are not supported"); - return JS_FALSE; + return NULL; #endif } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline) { JSRegExpStatics *res; + CHECK_REQUEST(cx); /* No locking required, cx is thread-private and input must be live. */ res = &cx->regExpStatics; res->input = input; res->multiline = multiline; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ClearRegExpStatics(JSContext *cx) { JSRegExpStatics *res; + CHECK_REQUEST(cx); /* No locking required, cx is thread-private and input must be live. */ res = &cx->regExpStatics; res->input = NULL; @@ -1859,33 +2480,94 @@ JS_ClearRegExpStatics(JSContext *cx) res->leftContext = res->rightContext = js_EmptySubString; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ClearRegExpRoots(JSContext *cx) { JSRegExpStatics *res; + CHECK_REQUEST(cx); /* No locking required, cx is thread-private and input must be live. */ res = &cx->regExpStatics; res->input = NULL; - res->execWrapper = NULL; } /* TODO: compile, execute, get/set other statics... */ /************************************************************************/ -#ifdef NETSCAPE_INTERNAL -PR_IMPLEMENT(void) -JS_SetCharSetInfo(JSContext *cx, const char *csName, int csId, - JSCharScanner scanner) +JS_PUBLIC_API(JSBool) +JS_IsExceptionPending(JSContext *cx) { - cx->charSetName = csName; - cx->charSetNameLength = csName ? strlen(csName) : 0; - cx->charSetId = csId; - cx->charScanner = scanner; + CHECK_REQUEST(cx); +#if JS_HAS_EXCEPTIONS + return (JSBool) cx->fp->exceptPending; +#else + return JS_FALSE; +#endif } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) +JS_GetPendingException(JSContext *cx, jsval *vp) +{ + CHECK_REQUEST(cx); +#if JS_HAS_EXCEPTIONS + if (!cx->fp->exceptPending) + return JS_FALSE; + *vp = cx->fp->exception; + return JS_TRUE; +#else + return JS_FALSE; +#endif +} + +JS_PUBLIC_API(void) +JS_SetPendingException(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); +#if JS_HAS_EXCEPTIONS + cx->fp->exceptPending = JS_TRUE; + cx->fp->exception = v; +#endif +} + +JS_PUBLIC_API(void) +JS_ClearPendingException(JSContext *cx) +{ + CHECK_REQUEST(cx); +#if JS_HAS_EXCEPTIONS + cx->fp->exceptPending = JS_FALSE; +#endif +} + +#ifdef JS_THREADSAFE +JS_PUBLIC_API(intN) +JS_GetContextThread(JSContext *cx) +{ + return cx->thread; +} + +JS_PUBLIC_API(intN) +JS_SetContextThread(JSContext *cx) +{ + intN old = cx->thread; + cx->thread = js_CurrentThreadId(); + return old; +} + +JS_PUBLIC_API(intN) +JS_ClearContextThread(JSContext *cx) +{ + intN old = cx->thread; + cx->thread = 0; + return old; +} +#endif + + +/************************************************************************/ + +#ifdef NETSCAPE_INTERNAL +JS_PUBLIC_API(JSBool) JS_IsAssigning(JSContext *cx) { JSStackFrame *fp; @@ -1900,6 +2582,7 @@ JS_IsAssigning(JSContext *cx) /************************************************************************/ #ifdef XP_PC +#include #if defined(XP_OS2_HACK) /*DSR031297 - the OS/2 equiv is dll_InitTerm, but I don't see the need for it*/ #else diff --git a/mozilla/js/src/jsapi.h b/mozilla/js/src/jsapi.h index 6d5d5c98d1e..998d9b22077 100644 --- a/mozilla/js/src/jsapi.h +++ b/mozilla/js/src/jsapi.h @@ -52,6 +52,7 @@ PR_BEGIN_EXTERN_C #define JSVAL_IS_BOOLEAN(v) (JSVAL_TAG(v) == JSVAL_BOOLEAN) #define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL) #define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID) +#define JSVAL_IS_PRIMITIVE(v) (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v)) /* Objects, strings, and doubles are GC'ed. */ #define JSVAL_IS_GCTHING(v) (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v)) @@ -88,9 +89,9 @@ PR_BEGIN_EXTERN_C #define JSVAL_TO_PRIVATE(v) ((void *)((v) & ~JSVAL_INT)) #define PRIVATE_TO_JSVAL(p) ((jsval)(p) | JSVAL_INT) -/* Property flags, set in JSPropertySpec and passed to API functions. */ +/* Property attributes, set in JSPropertySpec and passed to API functions. */ #define JSPROP_ENUMERATE 0x01 /* property is visible to for/in loop */ -#define JSPROP_READONLY 0x02 /* not settable: assignment is error */ +#define JSPROP_READONLY 0x02 /* not settable: assignment is no-op */ #define JSPROP_PERMANENT 0x04 /* property cannot be deleted */ #define JSPROP_EXPORTED 0x08 /* property is exported from object */ #define JSPROP_INDEX 0x20 /* name is actually (jsint) index */ @@ -100,6 +101,7 @@ PR_BEGIN_EXTERN_C /* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */ #define JSFUN_BOUND_METHOD 0x40 /* bind this to fun->object's parent */ #define JSFUN_GLOBAL_PARENT 0x80 /* reparent calls to cx->globalObject */ +#define JSFUN_FLAGS_MASK 0xc0 /* overlay JSPROP_*HACK attributes */ /* * Well-known JS values. The extern'd variables are initialized when the @@ -112,52 +114,108 @@ PR_BEGIN_EXTERN_C #define JSVAL_FALSE BOOLEAN_TO_JSVAL(JS_FALSE) #define JSVAL_TRUE BOOLEAN_TO_JSVAL(JS_TRUE) -/* Windows DLLs can't export data, so provide accessors for the above vars. */ -PR_EXTERN(jsval) +/* Don't want to export data, so provide accessors for non-inline jsvals. */ +extern JS_PUBLIC_API(jsval) JS_GetNaNValue(JSContext *cx); -PR_EXTERN(jsval) +extern JS_PUBLIC_API(jsval) JS_GetNegativeInfinityValue(JSContext *cx); -PR_EXTERN(jsval) +extern JS_PUBLIC_API(jsval) JS_GetPositiveInfinityValue(JSContext *cx); -PR_EXTERN(jsval) +extern JS_PUBLIC_API(jsval) JS_GetEmptyStringValue(JSContext *cx); -PR_EXTERN(JSBool) +/* + * Format is a string of the following characters (spaces are insignificant), + * specifying the tabulated type conversions: + * + * b JSBool Boolean + * c uint16/jschar ECMA uint16, Unicode char + * i int32 ECMA int32 + * u uint32 ECMA uint32 + * j int32 Rounded int32 (coordinate) + * d jsdouble IEEE double + * I jsdouble Integral IEEE double + * s char * C string + * S JSString * Unicode string + * o JSObject * Object reference + * f JSFunction * Function private + * v jsval Argument value (no conversion) + * * N/A Skip this argument (no vararg) + * / N/A End of required arguments + * + * The variable argument list after format must consist of &b, &c, &s, e.g., + * where those variables have the types given above. For the pointer types + * char *, JSString *, and JSObject *, the pointed-at memory returned belongs + * to the JS runtime, not to the calling native code. The runtimes promises + * to keep this memory valid so long as argv refers to allocated stack space + * (so long as the native function is active). + * + * Fewer arguments than format specifies may be passed only if there is a / + * in format after the last required argument specifier and argc is at least + * the number of required arguments. More arguments than format specifies + * may be passed without error; it is up to the caller to deal with trailing + * unconverted arguments. + */ +extern JS_PUBLIC_API(JSBool) +JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, + ...); + +extern JS_PUBLIC_API(JSBool) JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp); -PR_EXTERN(JSFunction *) +extern JS_PUBLIC_API(JSFunction *) JS_ValueToFunction(JSContext *cx, jsval v); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSFunction *) +JS_ValueToConstructor(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(JSString *) JS_ValueToString(JSContext *cx, jsval v); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); /* - * Convert a value to a number, then to an int32 if it fits (discarding any - * fractional part, but failing with an error if the double is out of range - * or unordered). + * Convert a value to a number, then to an int32, according to the ECMA rules + * for ToInt32. */ -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) +JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); + +/* + * Convert a value to a number, then to a uint32, according to the ECMA rules + * for ToUint32. + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); + +/* + * Convert a value to a number, then to an int32 if it fits by rounding to + * nearest; but failing with an error report if the double is out of range + * or unordered. + */ +extern JS_PUBLIC_API(JSBool) JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip); -PR_EXTERN(JSBool) +/* + * ECMA ToUint16, for mapping a jsval to a Unicode point. + */ +extern JS_PUBLIC_API(JSBool) JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); -PR_EXTERN(JSType) +extern JS_PUBLIC_API(JSType) JS_TypeOfValue(JSContext *cx, jsval v); -PR_EXTERN(const char *) +extern JS_PUBLIC_API(const char *) JS_GetTypeName(JSContext *cx, JSType type); /************************************************************************/ @@ -165,133 +223,236 @@ JS_GetTypeName(JSContext *cx, JSType type); /* * Initialization, locking, contexts, and memory allocation. */ -PR_EXTERN(JSRuntime *) -JS_Init(uint32 maxbytes); +#define JS_NewRuntime JS_Init +#define JS_DestroyRuntime JS_Finish +#define JS_LockRuntime JS_Lock +#define JS_UnlockRuntime JS_Unlock -PR_EXTERN(void) -JS_Finish(JSRuntime *rt); +extern JS_PUBLIC_API(JSRuntime *) +JS_NewRuntime(uint32 maxbytes); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) +JS_DestroyRuntime(JSRuntime *rt); + +extern JS_PUBLIC_API(void) +JS_ShutDown(void); + +#ifdef JS_THREADSAFE + +extern JS_PUBLIC_API(void) +JS_BeginRequest(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_EndRequest(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_SuspendRequest(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_ResumeRequest(JSContext *cx); + +#endif /* JS_THREADSAFE */ + +extern JS_PUBLIC_API(void) JS_Lock(JSRuntime *rt); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_Unlock(JSRuntime *rt); -PR_EXTERN(JSContext *) +extern JS_PUBLIC_API(JSContext *) JS_NewContext(JSRuntime *rt, size_t stacksize); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_DestroyContext(JSContext *cx); -PR_EXTERN(JSRuntime *) -JS_GetRuntime(JSContext *cx); - -PR_EXTERN(JSContext *) -JS_ContextIterator(JSRuntime *rt, JSContext **iterp); - -PR_EXTERN(JSVersion) -JS_GetVersion(JSContext *cx); - -PR_EXTERN(JSVersion) -JS_SetVersion(JSContext *cx, JSVersion version); - -PR_EXTERN(JSObject *) -JS_GetGlobalObject(JSContext *cx); +PR_EXTERN(void*) +JS_GetContextPrivate(JSContext *cx); PR_EXTERN(void) +JS_SetContextPrivate(JSContext *cx, void *pvt); + +extern JS_PUBLIC_API(JSRuntime *) +JS_GetRuntime(JSContext *cx); + +extern JS_PUBLIC_API(JSContext *) +JS_ContextIterator(JSRuntime *rt, JSContext **iterp); + +extern JS_PUBLIC_API(JSVersion) +JS_GetVersion(JSContext *cx); + +extern JS_PUBLIC_API(JSVersion) +JS_SetVersion(JSContext *cx, JSVersion version); + +extern JS_PUBLIC_API(JSObject *) +JS_GetGlobalObject(JSContext *cx); + +extern JS_PUBLIC_API(void) JS_SetGlobalObject(JSContext *cx, JSObject *obj); /* NB: This sets cx's global object to obj if it was null. */ -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_InitStandardClasses(JSContext *cx, JSObject *obj); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_GetScopeChain(JSContext *cx); -PR_EXTERN(void *) +extern JS_PUBLIC_API(void *) JS_malloc(JSContext *cx, size_t nbytes); -PR_EXTERN(void *) +extern JS_PUBLIC_API(void *) JS_realloc(JSContext *cx, void *p, size_t nbytes); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_free(JSContext *cx, void *p); -PR_EXTERN(char *) +extern JS_PUBLIC_API(char *) JS_strdup(JSContext *cx, const char *s); -PR_EXTERN(jsdouble *) +extern JS_PUBLIC_API(jsdouble *) JS_NewDouble(JSContext *cx, jsdouble d); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_AddRoot(JSContext *cx, void *rp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_RemoveRoot(JSContext *cx, void *rp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_AddNamedRoot(JSContext *cx, void *rp, const char *name); #ifdef DEBUG -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data); #endif -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_LockGCThing(JSContext *cx, void *thing); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_UnlockGCThing(JSContext *cx, void *thing); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_GC(JSContext *cx); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_MaybeGC(JSContext *cx); +extern JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallback(JSContext *cx, JSGCCallback cb); + /************************************************************************/ /* * Classes, objects, and properties. */ struct JSClass { - char *name; - uint32 flags; - JSPropertyOp addProperty; - JSPropertyOp delProperty; - JSPropertyOp getProperty; - JSPropertyOp setProperty; - JSEnumerateOp enumerate; - JSResolveOp resolve; - JSConvertOp convert; - JSFinalizeOp finalize; - prword spare[8]; + char *name; + uint32 flags; + + /* Mandatory non-null function pointer members. */ + JSPropertyOp addProperty; + JSPropertyOp delProperty; + JSPropertyOp getProperty; + JSPropertyOp setProperty; + JSEnumerateOp enumerate; + JSResolveOp resolve; + JSConvertOp convert; + JSFinalizeOp finalize; + + /* Optionally non-null members start here. */ + JSGetObjectOps getObjectOps; + JSCheckAccessOp checkAccess; + JSNative call; + JSNative construct; + JSXDRObjectOp xdrObject; + JSHasInstanceOp hasInstance; + prword spare[2]; }; #define JSCLASS_HAS_PRIVATE 0x01 /* class instances have private slot */ -#define JSCLASS_NEW_RESOLVE 0x02 /* class has JSNewResolveOp method */ +#define JSCLASS_NEW_ENUMERATE 0x02 /* class has JSNewEnumerateOp method */ +#define JSCLASS_NEW_RESOLVE 0x04 /* class has JSNewResolveOp method */ -PR_EXTERN(JSBool) +struct JSObjectOps { + /* Mandatory non-null function pointer members. */ + JSNewObjectMapOp newObjectMap; + JSObjectMapOp destroyObjectMap; + JSLookupPropOp lookupProperty; + JSDefinePropOp defineProperty; + JSPropertyIdOp getProperty; + JSPropertyIdOp setProperty; + JSAttributesOp getAttributes; + JSAttributesOp setAttributes; + JSPropertyIdOp deleteProperty; + JSConvertOp defaultValue; + JSNewEnumerateOp enumerate; + JSCheckAccessIdOp checkAccess; + + /* Optionally non-null members start here. */ + JSObjectOp thisObject; + JSPropertyRefOp dropProperty; + JSNative call; + JSNative construct; + JSXDRObjectOp xdrObject; + JSHasInstanceOp hasInstance; + prword spare[2]; +}; + +/* + * Classes that expose JSObjectOps via a non-null getObjectOps class hook may + * derive a property structure from this struct, return a pointer to it from + * lookupProperty and defineProperty, and use the pointer to avoid rehashing + * in getAttributes and setAttributes. + * + * The jsid type contains either an int jsval (see JSVAL_IS_INT above), or an + * internal pointer that is opaque to users of this API, but which users may + * convert from and to a jsval using JS_ValueToId and JS_IdToValue. + */ +struct JSProperty { + jsid id; +}; + +struct JSIdArray { + jsint length; + jsid vector[1]; /* actually, length jsid words */ +}; + +extern JS_PUBLIC_API(JSIdArray *) +JS_NewIdArray(JSContext *cx, jsint length); + +extern JS_PUBLIC_API(void) +JS_DestroyIdArray(JSContext *cx, JSIdArray *ida); + +extern JS_PUBLIC_API(JSBool) +JS_ValueToId(JSContext *cx, jsval v, jsid *idp); + +extern JS_PUBLIC_API(JSBool) +JS_IdToValue(JSContext *cx, jsid id, jsval *vp); + +#define JSRESOLVE_QUALIFIED 0x01 /* resolve a qualified property id */ +#define JSRESOLVE_ASSIGNING 0x02 /* resolve on the left of assignment */ + +extern JS_PUBLIC_API(JSBool) JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_EnumerateStub(JSContext *cx, JSObject *obj); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_FinalizeStub(JSContext *cx, JSObject *obj); struct JSConstDoubleSpec { @@ -319,136 +480,210 @@ struct JSFunctionSpec { JSNative call; uint8 nargs; uint8 flags; + uint16 extra; }; -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, JSClass *clasp, JSNative constructor, uintN nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs); -PR_EXTERN(JSClass *) +#ifdef JS_THREADSAFE +extern JS_PUBLIC_API(JSClass *) +JS_GetClass(JSContext *cx, JSObject *obj); +#else +JS_PUBLIC_API(JSClass *) JS_GetClass(JSObject *obj); +#endif -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); -PR_EXTERN(void *) +extern JS_PUBLIC_API(void *) JS_GetPrivate(JSContext *cx, JSObject *obj); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_SetPrivate(JSContext *cx, JSObject *obj, void *data); -PR_EXTERN(void *) +extern JS_PUBLIC_API(void *) JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_GetPrototype(JSContext *cx, JSObject *obj); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_GetParent(JSContext *cx, JSObject *obj); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_GetConstructor(JSContext *cx, JSObject *proto); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, - JSObject *proto, uintN flags); + JSObject *proto, uintN attrs); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN flags); + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); -PR_EXTERN(JSBool) +/* + * Determine the attributes (JSPROP_* flags) of a property on a given object. + * + * 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); + +/* + * Set the attributes of a property on a given object. + * + * 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); + +extern JS_PUBLIC_API(JSBool) JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, int8 tinyid, jsval value, JSPropertyOp getter, JSPropertyOp setter, - uintN flags); + uintN attrs); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, const char *alias); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSBool) +JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_DefineUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *rval); + +extern JS_PUBLIC_API(JSObject *) JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_IsArrayObject(JSContext *cx, JSObject *obj); -PR_EXTERN(JSBool) -JS_GetArrayLength(JSContext *cx, JSObject *obj, jsint *lengthp); +extern JS_PUBLIC_API(JSBool) +JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); -PR_EXTERN(JSBool) -JS_SetArrayLength(JSContext *cx, JSObject *obj, jsint length); +extern JS_PUBLIC_API(JSBool) +JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length); -PR_EXTERN(JSBool) -JS_HasLengthProperty(JSContext *cx, JSObject *obj); +extern JS_PUBLIC_API(JSBool) +JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN flags); + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index); -PR_EXTERN(void) +extern JS_PUBLIC_API(JSBool) +JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval); + +extern JS_PUBLIC_API(void) JS_ClearScope(JSContext *cx, JSObject *obj); +extern JS_PUBLIC_API(JSIdArray *) +JS_Enumerate(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp); + /************************************************************************/ /* * Security protocol. */ - typedef struct JSPrincipals { char *codebase; void *(*getPrincipalArray)(JSContext *cx, struct JSPrincipals *); - JSBool (*globalPrivilegesEnabled)(JSContext *cx, - struct JSPrincipals *); + JSBool (*globalPrivilegesEnabled)(JSContext *cx, struct JSPrincipals *); + /* Don't call "destroy"; use reference counting macros below. */ uintN refcount; void (*destroy)(JSContext *cx, struct JSPrincipals *); @@ -466,175 +701,187 @@ typedef struct JSPrincipals { /* * Functions and scripts. */ -PR_EXTERN(JSFunction *) +extern JS_PUBLIC_API(JSFunction *) JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, JSObject *parent, const char *name); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_GetFunctionObject(JSFunction *fun); -PR_EXTERN(const char *) +extern JS_PUBLIC_API(const char *) JS_GetFunctionName(JSFunction *fun); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs); -PR_EXTERN(JSFunction *) +extern JS_PUBLIC_API(JSFunction *) JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, - uintN nargs, uintN flags); + uintN nargs, uintN attrs); -PR_EXTERN(JSScript *) +extern JS_PUBLIC_API(JSObject *) +JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); + +extern JS_PUBLIC_API(JSScript *) JS_CompileScript(JSContext *cx, JSObject *obj, const char *bytes, size_t length, const char *filename, uintN lineno); -PR_EXTERN(JSScript *) +extern JS_PUBLIC_API(JSScript *) JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, size_t length, const char *filename, uintN lineno); -PR_EXTERN(JSScript *) +extern JS_PUBLIC_API(JSScript *) JS_CompileUCScript(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno); -PR_EXTERN(JSScript *) +extern JS_PUBLIC_API(JSScript *) JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, size_t length, const char *filename, uintN lineno); #ifdef JSFILE -PR_EXTERN(JSScript *) +extern JS_PUBLIC_API(JSScript *) JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename); #endif -PR_EXTERN(void) +extern JS_PUBLIC_API(JSObject *) +JS_NewScriptObject(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(void) JS_DestroyScript(JSContext *cx, JSScript *script); -PR_EXTERN(JSFunction *) +extern JS_PUBLIC_API(JSFunction *) JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, uintN nargs, const char **argnames, const char *bytes, size_t length, const char *filename, uintN lineno); -PR_EXTERN(JSFunction *) +extern JS_PUBLIC_API(JSFunction *) JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *name, uintN nargs, const char **argnames, const char *bytes, size_t length, const char *filename, uintN lineno); -PR_EXTERN(JSFunction *) +extern JS_PUBLIC_API(JSFunction *) JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, uintN nargs, const char **argnames, const jschar *chars, size_t length, const char *filename, uintN lineno); -PR_EXTERN(JSFunction *) +extern JS_PUBLIC_API(JSFunction *) JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *name, uintN nargs, const char **argnames, const jschar *chars, size_t length, const char *filename, uintN lineno); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN indent); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_EvaluateScript(JSContext *cx, JSObject *obj, const char *bytes, uintN length, const char *filename, uintN lineno, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, uintN length, const char *filename, uintN lineno, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_EvaluateUCScript(JSContext *cx, JSObject *obj, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval *argv, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *argv, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, jsval *rval); -PR_EXTERN(JSBranchCallback) +extern JS_PUBLIC_API(JSBranchCallback) JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx); +extern JS_PUBLIC_API(JSBool) +JS_IsConstructing(JSContext *cx); + /************************************************************************/ /* * Strings. */ -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_NewString(JSContext *cx, char *bytes, size_t length); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_NewStringCopyN(JSContext *cx, const char *s, size_t n); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_NewStringCopyZ(JSContext *cx, const char *s); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_InternString(JSContext *cx, const char *s); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_NewUCString(JSContext *cx, jschar *chars, size_t length); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) JS_NewUCStringCopyZ(JSContext *cx, const jschar *s); -PR_EXTERN(JSString *) +extern JS_PUBLIC_API(JSString *) +JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length); + +extern JS_PUBLIC_API(JSString *) JS_InternUCString(JSContext *cx, const jschar *s); -PR_EXTERN(char *) +extern JS_PUBLIC_API(char *) JS_GetStringBytes(JSString *str); -PR_EXTERN(jschar *) +extern JS_PUBLIC_API(jschar *) JS_GetStringChars(JSString *str); -PR_EXTERN(size_t) +extern JS_PUBLIC_API(size_t) JS_GetStringLength(JSString *str); -PR_EXTERN(intN) +extern JS_PUBLIC_API(intN) JS_CompareStrings(JSString *str1, JSString *str2); /************************************************************************/ @@ -646,15 +893,16 @@ JS_CompareStrings(JSString *str1, JSString *str2); /* * Report an exception represented by the sprintf-like conversion of format * and its arguments. This exception message string is passed to a pre-set - * MochaErrorReporter function (see immediately below). + * JSErrorReporter function (set by JS_SetErrorReporter; see jspubtd.h for + * the JSErrorReporter typedef). */ -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ReportError(JSContext *cx, const char *format, ...); /* * Complain when out of memory. */ -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ReportOutOfMemory(JSContext *cx); struct JSErrorReport { @@ -666,7 +914,7 @@ struct JSErrorReport { const jschar *uctokenptr;/* unicode (original) token pointer */ }; -PR_EXTERN(JSErrorReporter) +extern JS_PUBLIC_API(JSErrorReporter) JS_SetErrorReporter(JSContext *cx, JSErrorReporter er); /************************************************************************/ @@ -677,41 +925,66 @@ JS_SetErrorReporter(JSContext *cx, JSErrorReporter er); #define JSREG_FOLD 0x01 /* fold uppercase to lowercase */ #define JSREG_GLOB 0x02 /* global exec, creates array of matches */ -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ClearRegExpStatics(JSContext *cx); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ClearRegExpRoots(JSContext *cx); -/* TODO: compile, execute, get/set other statics... */ +/* TODO: compile, exec, get/set other statics... */ + +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_IsExceptionPending(JSContext *cx); + +extern JS_PUBLIC_API(JSBool) +JS_GetPendingException(JSContext *cx, jsval *vp); + +extern JS_PUBLIC_API(void) +JS_SetPendingException(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(void) +JS_ClearPendingException(JSContext *cx); + +#ifdef JS_THREADSAFE +/* + * 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 + * an opaque value. This value is provided for comparison to 0, which + * indicates that ClearContextThread has been called on this context + * since the last SetContextThread, or non-0, which indicates the opposite. + */ + +extern JS_PUBLIC_API(intN) +JS_GetContextThread(JSContext *cx); + +extern JS_PUBLIC_API(intN) +JS_SetContextThread(JSContext *cx); + +extern JS_PUBLIC_API(intN) +JS_ClearContextThread(JSContext *cx); +#endif /************************************************************************/ #ifdef NETSCAPE_INTERNAL -/* - * This function and related data are compiled only ifdef NETSCAPE_INTERNAL. - * A future version of the JS runtime will use Unicode strings; the API will - * provide JS_EncodeString (from Unicode to a given character set encoding) - * and JS_DecodeString (reverse) functions. - */ -PR_EXTERN(void) -JS_SetCharSetInfo(JSContext *cx, const char *csName, int csId, - JSCharScanner scanner); - /* * Returns true if a script is executing and its current bytecode is a set * (assignment) operation. */ -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_IsAssigning(JSContext *cx); #endif diff --git a/mozilla/js/src/jsarray.c b/mozilla/js/src/jsarray.c index bb593cc3da7..7dc45223012 100644 --- a/mozilla/js/src/jsarray.c +++ b/mozilla/js/src/jsarray.c @@ -35,154 +35,235 @@ #include "jslock.h" #include "jsnum.h" #include "jsobj.h" -#include "jsopcode.h" -#include "jsscope.h" #include "jsstr.h" static JSBool -ValueToLength(JSContext *cx, jsval v, jsint *lengthp) +IdIsIndex(jsid id, jsuint *indexp) { jsint i; jsdouble d; + JSBool ok; + JSString *str; + jschar *ep; + + if (JSVAL_IS_INT(id)) { + i = JSVAL_TO_INT(id); + if (i < 0) + return JS_FALSE; + *indexp = (jsuint)i; + return JS_TRUE; + } + + /* It must be a string. */ + str = JSVAL_TO_STRING(id); + ok = (str->length > 0 && JS7_ISDEC(str->chars[0]) && + js_strtod(str->chars, &ep, &d)); + if (!ok || d < 0 || js_DoubleToInteger(d) != d || d > 4294967294.0) + return JS_FALSE; + + *indexp = (jsuint)d; + return JS_TRUE; +} + +static JSBool +ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) +{ + jsint i; + + /* 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 + * javascript. + */ + if (JSVAL_IS_INT(v)) { + i = JSVAL_TO_INT(v); + /* jsuint cast does ToUint32 */ + *lengthp = (jsuint)i; + return JS_TRUE; + } else 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). + */ + return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp); + } else { + return JS_FALSE; + } +} + + +static JSBool +ValueToIndex(JSContext *cx, jsval v, jsuint *lengthp) +{ + jsint i; if (JSVAL_IS_INT(v)) { i = JSVAL_TO_INT(v); - *lengthp = (i < 0) ? 0 : i; + /* jsuint cast does ToUint32. */ + *lengthp = (jsuint)i; + return JS_TRUE; + } + return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp); +} + +JSBool +js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + jsid id; + jsint i; + jsval v; + + id = (jsid) cx->runtime->atomState.lengthAtom; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + + /* Short-circuit, because js_ValueToECMAUint32 fails when + * called during init time. + */ + if (JSVAL_IS_INT(v)) { + i = JSVAL_TO_INT(v); + /* jsuint cast does ToUint32. */ + *lengthp = (jsuint)i; + return JS_TRUE; + } + return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp); +} + +static JSBool +IndexToValue(JSContext *cx, jsuint length, jsval *vp) +{ + if (length <= JSVAL_INT_MAX) { + *vp = INT_TO_JSVAL(length); + return JS_TRUE; + } + return js_NewDoubleValue(cx, (jsdouble)length, vp); +} + +static JSBool +IndexToId(JSContext *cx, jsuint length, jsid *idp) +{ + JSString *str; + JSAtom *atom; + + if (length <= JSVAL_INT_MAX) { + *idp = (jsid) INT_TO_JSVAL(length); } else { - if (JSVAL_IS_DOUBLE(v)) { - d = *JSVAL_TO_DOUBLE(v); - } else { - if (!js_ValueToNumber(cx, v, &d)) - return JS_FALSE; - } - *lengthp = (d < 0 || d > JSVAL_INT_MAX || JSDOUBLE_IS_NaN(d)) - ? 0 - : (jsint)d; + str = js_NumberToString(cx, (jsdouble)length); + if (!str) + return JS_FALSE; + atom = js_AtomizeString(cx, str, 0); + if (!atom) + return JS_FALSE; + *idp = (jsid)atom; + } return JS_TRUE; } -static JSProperty * -GetLengthProperty(JSContext *cx, JSObject *obj, jsint *lengthp) +JSBool +js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) { - JSProperty *prop; jsval v; + jsid id; - PR_ASSERT(JS_IS_LOCKED(cx)); - prop = js_GetProperty(cx, obj, - (jsval)cx->runtime->atomState.lengthAtom, - &v); - if (!prop || !ValueToLength(cx, v, lengthp)) - return NULL; - return prop; + if (!IndexToValue(cx, length, &v)) + return JS_FALSE; + id = (jsid) cx->runtime->atomState.lengthAtom; + return OBJ_SET_PROPERTY(cx, obj, id, &v); } -static JSProperty * -SetLengthProperty(JSContext *cx, JSObject *obj, jsint length) +JSBool +js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) { + JSErrorReporter older; + jsid id; + JSBool ok; jsval v; - PR_ASSERT(JS_IS_LOCKED(cx)); - v = INT_TO_JSVAL(length); - return js_SetProperty(cx, obj, - (jsval)cx->runtime->atomState.lengthAtom, - &v); + older = JS_SetErrorReporter(cx, NULL); + id = (jsid) cx->runtime->atomState.lengthAtom; + ok = OBJ_GET_PROPERTY(cx, obj, id, &v); + JS_SetErrorReporter(cx, older); + if (!ok) + return JS_FALSE; + return ValueIsLength(cx, v, lengthp); +} + +/* + * This get function is specific to Array.prototype.length and other array + * instance length properties. It calls back through the class get function + * in case some magic happens there (see call_getProperty in jsfun.c). + */ +static JSBool +array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp); } static JSBool array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - JSBool ok; - jsint newlen, oldlen, slot; + jsuint newlen, oldlen, slot; + jsid id2; jsval junk; - JS_LOCK(cx); - ok = ValueToLength(cx, *vp, &newlen); - if (!ok) - goto out; - if (!GetLengthProperty(cx, obj, &oldlen)) { - ok = JS_FALSE; - goto out; - } + if (!ValueToIndex(cx, *vp, &newlen)) + return JS_FALSE; + if (!js_GetLengthProperty(cx, obj, &oldlen)) + return JS_FALSE; for (slot = newlen; slot < oldlen; slot++) { - ok = js_DeleteProperty(cx, obj, INT_TO_JSVAL(slot), &junk); - if (!ok) - break; + if (!IndexToId(cx, slot, &id2)) + return JS_FALSE; + if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) + return JS_FALSE; } -out: - JS_UNLOCK(cx); - return ok; + return IndexToValue(cx, newlen, vp); } static JSBool array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - jsint index, length; - JSProperty *prop; + jsuint index, length; - if (!JSVAL_IS_INT(id)) + if (!(IdIsIndex(id, &index))) return JS_TRUE; - index = JSVAL_TO_INT(id); - JS_LOCK(cx); - prop = GetLengthProperty(cx, obj, &length); - if (prop && index >= length) { + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + if (index >= length) { length = index + 1; - if (prop->object == obj && prop->setter == array_length_setter) - obj->slots[prop->slot] = INT_TO_JSVAL(length); - else - prop = SetLengthProperty(cx, obj, length); + return js_SetLengthProperty(cx, obj, length); } - JS_UNLOCK(cx); - return (prop != NULL); -} - -static JSBool -array_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint index, length; - JSProperty *prop; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - index = JSVAL_TO_INT(id); - JS_LOCK(cx); - prop = GetLengthProperty(cx, obj, &length); - if (prop && index == length - 1) { - length = index; - if (prop->object == obj && prop->setter == array_length_setter) - obj->slots[prop->slot] = INT_TO_JSVAL(length); - else - prop = SetLengthProperty(cx, obj, length); - } - JS_UNLOCK(cx); - return (prop != NULL); + return JS_TRUE; } static JSBool array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { - JSProperty *prop; - jsint length; + jsuint length; - JS_LOCK_VOID(cx, prop = GetLengthProperty(cx, obj, &length)); - if (!prop) - return JS_FALSE; - switch (type) { - case JSTYPE_NUMBER: - *vp = INT_TO_JSVAL(length); - return JS_TRUE; - case JSTYPE_BOOLEAN: - *vp = BOOLEAN_TO_JSVAL(length > 0); - return JS_TRUE; - default: - return JS_TRUE; + if (cx->version == JSVERSION_1_2) { + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + switch (type) { + case JSTYPE_NUMBER: + return IndexToValue(cx, length, vp); + case JSTYPE_BOOLEAN: + *vp = BOOLEAN_TO_JSVAL(length > 0); + return JS_TRUE; + default: + return JS_TRUE; + } } + js_TryValueOf(cx, obj, type, vp); + return JS_TRUE; } JSClass js_ArrayClass = { "Array", 0, - array_addProperty, array_delProperty, JS_PropertyStub, JS_PropertyStub, + array_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, array_convert, JS_FinalizeStub }; @@ -190,43 +271,36 @@ static JSBool array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, jsval *rval) { - JSProperty *prop; + JSBool ok; jsval v; - jsint length, index; - jschar *chars; + jsuint length, index; + jschar *chars, *ochars; size_t nchars, growth, seplen; const jschar *sepstr; - const char *quote; - JSBool ok; JSString *str; -#if JS_HAS_SHARP_VARS PRHashEntry *he; -#else - jsval lenval; -#endif - PR_ASSERT(JS_IS_LOCKED(cx)); - prop = GetLengthProperty(cx, obj, &length); - if (!prop) + ok = js_GetLengthProperty(cx, obj, &length); + if (!ok) return JS_FALSE; -#if !JS_HAS_SHARP_VARS - lenval = INT_TO_JSVAL(length); -#endif - ok = JS_TRUE; + if (literalize) { -#if JS_HAS_SHARP_VARS - he = js_EnterSharpObject(cx, obj, &chars); + he = js_EnterSharpObject(cx, obj, NULL, &chars); if (!he) return JS_FALSE; if (IS_SHARP(he)) { +#if JS_HAS_SHARP_VARS nchars = js_strlen(chars); +#else + chars[0] = '['; + chars[1] = ']'; + chars[2] = 0; + nchars = 2; +#endif goto make_string; } - MAKE_SHARP(he); -#else - chars = NULL; -#endif + /* * Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the * terminating 0. @@ -235,12 +309,17 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, if (!chars) { nchars = 0; chars = malloc(growth); + if (!chars) + goto done; } else { + MAKE_SHARP(he); nchars = js_strlen(chars); - chars = realloc(chars, nchars * sizeof(jschar) + growth); + chars = realloc((ochars = chars), nchars * sizeof(jschar) + growth); + if (!chars) { + free(ochars); + goto done; + } } - if (!chars) - goto done; chars[nchars++] = '['; } else { if (length == 0) { @@ -259,40 +338,31 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, if (!ok) goto done; - /* If v is a string, it needs to be quoted and escaped. */ - quote = NULL; - if (JSVAL_IS_VOID(v)) { + if (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)) { str = cx->runtime->emptyString; } else { -#if !JS_HAS_SHARP_VARS - /* Avoid recursive divergence by setting length to 0. */ - obj->slots[prop->slot] = JSVAL_ZERO; -#endif - str = js_ValueToString(cx, v); -#if !JS_HAS_SHARP_VARS - obj->slots[prop->slot] = lenval; -#endif + str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v); if (!str) { ok = JS_FALSE; goto done; } - if (literalize && JSVAL_IS_STRING(v)) { - quote = "\""; - str = js_EscapeString(cx, str, *quote); - if (!str) { - ok = JS_FALSE; - goto done; - } - } } /* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */ growth = (nchars + (sepstr ? seplen : 0) + - (quote ? 2 : 0) + str->length + + str->length + 3 + 1) * sizeof(jschar); - chars = chars ? realloc(chars, growth) : malloc(growth); - if (!chars) - goto done; + if (!chars) { + chars = malloc(growth); + if (!chars) + goto done; + } else { + chars = realloc((ochars = chars), growth); + if (!chars) { + free(ochars); + goto done; + } + } if (sepstr) { js_strncpy(&chars[nchars], sepstr, seplen); @@ -300,15 +370,11 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, } sepstr = sep->chars; - if (quote) - chars[nchars++] = *quote; js_strncpy(&chars[nchars], str->chars, str->length); nchars += str->length; - if (quote) - chars[nchars++] = *quote; } -done: + done: if (literalize) { if (chars) { if (JSVAL_IS_VOID(v)) { @@ -317,9 +383,7 @@ done: } chars[nchars++] = ']'; } -#if JS_HAS_SHARP_VARS - js_LeaveSharpObject(cx); -#endif + js_LeaveSharpObject(cx, NULL); } if (!ok) { if (chars) @@ -327,9 +391,7 @@ done: return ok; } -#if JS_HAS_SHARP_VARS make_string: -#endif if (!chars) { JS_ReportOutOfMemory(cx); return JS_FALSE; @@ -349,6 +411,15 @@ static jschar comma_ucstr[] = {',', 0}; static JSString comma_space = {2, comma_space_ucstr}; static JSString comma = {1, comma_ucstr}; +#if JS_HAS_TOSOURCE +static JSBool +array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return array_join_sub(cx, obj, &comma_space, JS_TRUE, rval); +} +#endif + static JSBool array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) @@ -359,33 +430,23 @@ array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, * JS1.2 arrays convert to array literals, with a comma followed by a space * between each element. */ - literalize = (cx->version >= JSVERSION_1_2); - JS_LOCK_AND_RETURN_BOOL(cx, - array_join_sub(cx, obj, - literalize ? &comma_space : &comma, - literalize, rval)); + literalize = (cx->version == JSVERSION_1_2); + return array_join_sub(cx, obj, literalize ? &comma_space : &comma, + literalize, rval); } static JSBool array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; - JSBool ok; - JS_LOCK(cx); - if (argc == 0) { - ok = array_join_sub(cx, obj, &comma, JS_FALSE, rval); - } else { - str = js_ValueToString(cx, argv[0]); - if (!str) { - ok = JS_FALSE; - } else { - argv[0] = STRING_TO_JSVAL(str); - ok = array_join_sub(cx, obj, str, JS_FALSE, rval); - } - } - JS_UNLOCK(cx); - return ok; + if (argc == 0) + return array_join_sub(cx, obj, &comma, JS_FALSE, rval); + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return array_join_sub(cx, obj, str, JS_FALSE, rval); } #if !JS_HAS_MORE_PERL_FUN @@ -393,30 +454,36 @@ static JSBool array_nyi(JSContext *cx, const char *what) { JS_ReportError(cx, "sorry, Array.prototype.%s is not yet implemented", - what); + what); return JS_FALSE; } #endif static JSBool -InitArrayObject(JSContext *cx, JSObject *obj, jsint length, jsval *vector) +InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) { - jsint index; + jsval v; + jsid id; + jsuint index; - PR_ASSERT(JS_IS_LOCKED(cx)); - if (!js_DefineProperty(cx, obj, - (jsval)cx->runtime->atomState.lengthAtom, - INT_TO_JSVAL(length), - JS_PropertyStub, array_length_setter, - JSPROP_PERMANENT)) { - return JS_FALSE; + if (!IndexToValue(cx, length, &v)) + return JS_FALSE; + id = (jsid) cx->runtime->atomState.lengthAtom; + if (!js_DefineProperty(cx, obj, id, v, + array_length_getter, array_length_setter, + JSPROP_PERMANENT, + NULL)) { + return JS_FALSE; } if (!vector) return JS_TRUE; for (index = 0; index < length; index++) { - if (!js_DefineProperty(cx, obj, INT_TO_JSVAL(index), vector[index], + if (!IndexToId(cx, index, &id)) + return JS_FALSE; + if (!js_DefineProperty(cx, obj, id, vector[index], JS_PropertyStub, JS_PropertyStub, - JSPROP_ENUMERATE)) { + JSPROP_ENUMERATE, + NULL)) { return JS_FALSE; } } @@ -427,36 +494,31 @@ static JSBool array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - jsint len, i; - jsval *vec, *vp; - JSBool ok; - JSProperty *prop; + jsuint len, half, i; + jsid id, id2; + jsval v, v2; - JS_LOCK(cx); - if (!GetLengthProperty(cx, obj, &len) || - !(vec = JS_malloc(cx, (size_t) len * sizeof *vec))) { - ok = JS_FALSE; - goto out; + if (!js_GetLengthProperty(cx, obj, &len)) + return JS_FALSE; + + half = len / 2; + for (i = 0; i < half; i++) { + if (!IndexToId(cx, i, &id)) + return JS_FALSE; + if (!IndexToId(cx, len - i - 1, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id2, &v2)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &v2)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; } - for (i = 0; i < len; i++) - vec[i] = JSVAL_VOID; - for (prop = obj->map->props; prop; prop = prop->next) { - if (!(prop->flags & JSPROP_TINYIDHACK) && - JSVAL_IS_INT(prop->id) && - (jsuint)(i = JSVAL_TO_INT(prop->id)) < (jsuint)len) { - vp = &vec[len - i - 1]; - *vp = prop->object->slots[prop->slot]; - } - } - - ok = InitArrayObject(cx, obj, len, vec); - if (ok) - *rval = OBJECT_TO_JSVAL(obj); - JS_free(cx, vec); -out: - JS_UNLOCK(cx); - return ok; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; } typedef struct QSortArgs { @@ -532,7 +594,7 @@ typedef struct CompareArgs { static int sort_compare(const void *a, const void *b, void *arg) { - const jsval *avp = a, *bvp = b; + jsval av = *(const jsval *)a, bv = *(const jsval *)b; CompareArgs *ca = arg; JSContext *cx = ca->context; jsdouble cmp = -1; @@ -543,22 +605,23 @@ sort_compare(const void *a, const void *b, void *arg) if (fval == JSVAL_NULL) { JSString *astr, *bstr; - if (*avp == *bvp) { + if (av == bv) { cmp = 0; - } else if (*avp == JSVAL_VOID || *bvp == JSVAL_VOID) { + } else if (av == JSVAL_VOID || bv == JSVAL_VOID) { /* Put undefined properties at the end. */ - cmp = (*avp == JSVAL_VOID) ? 1 : -1; - } else if ((astr = js_ValueToString(cx, *avp)) && - (bstr = js_ValueToString(cx, *bvp))) { + cmp = (av == JSVAL_VOID) ? 1 : -1; + } else if ((astr = js_ValueToString(cx, av)) != NULL && + (bstr = js_ValueToString(cx, bv)) != NULL) { cmp = js_CompareStrings(astr, bstr); } else { ca->status = JS_FALSE; } } else { - argv[0] = *avp; - argv[1] = *bvp; - ok = js_Call(cx, OBJ_GET_PARENT(JSVAL_TO_OBJECT(fval)), fval, 2, argv, - &rval); + argv[0] = av; + argv[1] = bv; + ok = js_CallFunctionValue(cx, + OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fval)), + fval, 2, argv, &rval); if (ok) ok = js_ValueToNumber(cx, rval, &cmp); if (!ok) @@ -567,47 +630,48 @@ sort_compare(const void *a, const void *b, void *arg) return (int)cmp; } +/* XXXmccabe do the sort helper functions need to take int? (Or can we claim + * that 2^32 * 32 is too large to worry about?) Something dumps when I change + * to unsigned int; is qsort using -1 as a fencepost? + */ static JSBool array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fval; CompareArgs ca; - jsint len, i; + jsuint len, i; jsval *vec; - JSProperty *prop; + jsid id; if (argc > 0) { - if (JS_TypeOfValue(cx, argv[0]) != JSTYPE_FUNCTION) { - /* XXX JS_ConvertValue shouldn't convert closure */ - if (!JS_ConvertValue(cx, argv[0], JSTYPE_FUNCTION, &argv[0])) - return JS_FALSE; + if (JSVAL_IS_PRIMITIVE(argv[0])) { + JS_ReportError(cx, "invalid Array.prototype.sort argument"); + return JS_FALSE; } fval = argv[0]; } else { fval = JSVAL_NULL; } - JS_LOCK(cx); - if (!GetLengthProperty(cx, obj, &len) || - !(vec = JS_malloc(cx, (size_t) len * sizeof *vec))) { - ca.status = JS_FALSE; - goto out; - } + if (!js_GetLengthProperty(cx, obj, &len)) + return JS_FALSE; + vec = JS_malloc(cx, (size_t) len * sizeof(jsval)); + if (!vec) + return JS_FALSE; - for (i = 0; i < len; i++) - vec[i] = JSVAL_VOID; - for (prop = obj->map->props; prop; prop = prop->next) { - if (!(prop->flags & JSPROP_TINYIDHACK) && - JSVAL_IS_INT(prop->id) && - (jsuint)(i = JSVAL_TO_INT(prop->id)) < (jsuint)len) { - vec[i] = prop->object->slots[prop->slot]; - } + for (i = 0; i < len; i++) { + ca.status = IndexToId(cx, i, &id); + if (!ca.status) + goto out; + ca.status = OBJ_GET_PROPERTY(cx, obj, id, &vec[i]); + if (!ca.status) + goto out; } ca.context = cx; ca.fval = fval; ca.status = JS_TRUE; - if (!js_qsort(vec, (size_t) len, (size_t) sizeof *vec, sort_compare, &ca)) { + if (!js_qsort(vec, (size_t) len, sizeof(jsval), sort_compare, &ca)) { JS_ReportOutOfMemory(cx); ca.status = JS_FALSE; } @@ -617,9 +681,9 @@ array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (ca.status) *rval = OBJECT_TO_JSVAL(obj); } - JS_free(cx, vec); out: - JS_UNLOCK(cx); + if (vec) + JS_free(cx, vec); return ca.status; } @@ -671,20 +735,17 @@ static JSBool array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #if JS_HAS_MORE_PERL_FUN - jsint length; + jsuint length; uintN i; - jsval id; - JSBool ok; + jsid id; - JS_LOCK(cx); - if (!GetLengthProperty(cx, obj, &length)) + if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; for (i = 0; i < argc; i++) { - id = INT_TO_JSVAL(length + i); - if (!js_SetProperty(cx, obj, id, &argv[i])) { - ok = JS_FALSE; - goto out; - } + if (!IndexToId(cx, length + i, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) + return JS_FALSE; } /* @@ -692,13 +753,13 @@ array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * return the new array length. */ length += argc; - *rval = (cx->version == JSVERSION_1_2) - ? (argc ? argv[argc-1] : JSVAL_VOID) - : INT_TO_JSVAL(length); - ok = SetLengthProperty(cx, obj, length) != NULL; -out: - JS_UNLOCK(cx); - return ok; + if (cx->version == JSVERSION_1_2) { + *rval = argc ? argv[argc-1] : JSVAL_VOID; + } else { + if (!IndexToValue(cx, length, rval)) + return JS_FALSE; + } + return js_SetLengthProperty(cx, obj, length); #else return array_nyi(cx, "push"); #endif @@ -708,36 +769,25 @@ static JSBool array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #if JS_HAS_MORE_PERL_FUN - jsint index; - JSBool ok; - jsval id, junk; - JSProperty *prop; + jsuint index; + jsid id; + jsval junk; - JS_LOCK(cx); - if (!GetLengthProperty(cx, obj, &index)) { - ok = JS_FALSE; - } else if (index == 0) { - ok = JS_TRUE; - } else { + if (!js_GetLengthProperty(cx, obj, &index)) + return JS_FALSE; + if (index > 0) { index--; - id = INT_TO_JSVAL(index); + if (!IndexToId(cx, index, &id)) + return JS_FALSE; - /* Get the to-be-deleted property's value into rval ASAP. */ - prop = js_GetProperty(cx, obj, id, rval); - if (!prop) { - ok = JS_FALSE; - goto out; - } + /* Get the to-be-deleted property's value into rval. */ + if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) + return JS_FALSE; - ok = js_DeleteProperty2(cx, obj, prop, id, &junk); - if (!ok) - goto out; - if (!SetLengthProperty(cx, obj, index)) - ok = JS_FALSE; + if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) + return JS_FALSE; } -out: - JS_UNLOCK(cx); - return ok; + return js_SetLengthProperty(cx, obj, index); #else return array_nyi(cx, "pop"); #endif @@ -747,53 +797,41 @@ static JSBool array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #if JS_HAS_MORE_PERL_FUN - jsint length, i; - JSBool ok; - jsval id, id2, v, junk; - JSProperty *prop; + jsuint length, i; + jsid id, id2; + jsval v, junk; - JS_LOCK(cx); - if (!GetLengthProperty(cx, obj, &length)) { - ok = JS_FALSE; - } else if (length == 0) { - ok = JS_TRUE; - } else { + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + if (length > 0) { length--; id = JSVAL_ZERO; /* Get the to-be-deleted property's value into rval ASAP. */ - prop = js_GetProperty(cx, obj, id, rval); - if (!prop) { - ok = JS_FALSE; - goto out; - } + if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) + return JS_FALSE; /* - * Slide down the array above the first element. Leave prop and - * id set to point to the last element. + * Slide down the array above the first element. */ if (length > 0) { for (i = 1; i <= length; i++) { - id = INT_TO_JSVAL(i); - id2 = INT_TO_JSVAL(i - 1); - prop = js_GetProperty(cx, obj, id, &v); - if (!prop || !js_SetProperty(cx, obj, id2, &v)) { - ok = JS_FALSE; - goto out; - } + if (!IndexToId(cx, i, &id)) + return JS_FALSE; + if (!IndexToId(cx, i - 1, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; } } /* Delete the only or last element. */ - ok = js_DeleteProperty2(cx, obj, prop, id, &junk); - if (!ok) - goto out; - if (!SetLengthProperty(cx, obj, length)) - ok = JS_FALSE; + if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) + return JS_FALSE; } -out: - JS_UNLOCK(cx); - return ok; + return js_SetLengthProperty(cx, obj, length); #else return array_nyi(cx, "shift"); #endif @@ -804,52 +842,43 @@ array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #if JS_HAS_MORE_PERL_FUN - jsint length, last; + jsuint length, last; uintN i; - jsval id, id2, v; - JSProperty *prop; - JSBool ok; + jsid id, id2; + jsval v; - JS_LOCK(cx); - if (!GetLengthProperty(cx, obj, &length)) { - ok = JS_FALSE; - goto out; - } + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; if (argc > 0) { /* Slide up the array to make room for argc at the bottom. */ if (length > 0) { - for (last = length - 1; last >= 0; last--) { - id = INT_TO_JSVAL(last); - id2 = INT_TO_JSVAL(last + argc); - prop = js_GetProperty(cx, obj, id, &v); - if (!prop || !js_SetProperty(cx, obj, id2, &v)) { - ok = JS_FALSE; - goto out; - } - } + last = length; + while (last--) { + if (!IndexToId(cx, last, &id)) + return JS_FALSE; + if (!IndexToId(cx, last + argc, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; + } } /* Copy from argv to the bottom of the array. */ for (i = 0; i < argc; i++) { - id = INT_TO_JSVAL(i); - if (!js_SetProperty(cx, obj, id, &argv[i])) { - ok = JS_FALSE; - goto out; - } + if (!IndexToId(cx, i, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) + return JS_FALSE; } /* Follow Perl by returning the new array length. */ length += argc; - if (!SetLengthProperty(cx, obj, length)) { - ok = JS_FALSE; - goto out; - } + if (!js_SetLengthProperty(cx, obj, length)) + return JS_FALSE; } - *rval = INT_TO_JSVAL(length); - ok = JS_TRUE; -out: - JS_UNLOCK(cx); - return ok; + return IndexToValue(cx, length, rval); #else return array_nyi(cx, "unshift"); #endif @@ -859,35 +888,31 @@ static JSBool array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #if JS_HAS_MORE_PERL_FUN - jsint length, begin, end, count, delta, last; + jsuint length, begin, end, count, delta, last; uintN i; - JSBool ok; jsdouble d; - jsval id, id2, v; + jsid id, id2; + jsval v; JSObject *obj2; - JSProperty *prop; /* Nothing to do if no args. Otherwise lock and load length. */ if (argc == 0) return JS_TRUE; - JS_LOCK(cx); - if (!GetLengthProperty(cx, obj, &length)) { - ok = JS_FALSE; - goto out; - } + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; /* Convert the first argument into a starting index. */ - ok = js_ValueToNumber(cx, *argv, &d); - if (!ok) - goto out; - begin = (jsint)d; - if (begin < 0) { - begin += length; - if (begin < 0) - begin = 0; - } else if (begin > length) { - begin = length; + if (!js_ValueToNumber(cx, *argv, &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) { + d += length; + if (d < 0) + d = 0; + } else if (d > length) { + d = length; } + begin = (jsuint)d; /* d has been clamped to uint32 */ argc--; argv++; @@ -897,96 +922,105 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) count = delta; end = length; } else { - ok = js_ValueToNumber(cx, *argv, &d); - if (!ok) - goto out; - count = (jsint)d; - if (count < 0) - count = 0; - else if (count > delta) - count = delta; + if (!js_ValueToNumber(cx, *argv, &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) + d = 0; + else if (d > delta) + d = delta; + count = (jsuint)d; end = begin + count; argc--; argv++; } - /* If there are elements to remove, put them into the return value. */ - if (count > 0) { - if (count == 1 && cx->version == JSVERSION_1_2) { + if (count == 1 && cx->version == JSVERSION_1_2) { + /* + * JS lacks "list context", whereby in Perl one turns the single + * scalar that's spliced out into an array just by assigning it to + * @single instead of $single, or by using it as Perl push's first + * argument, for instance. + * + * JS1.2 emulated Perl too closely and returned a non-Array for + * the single-splice-out case, requiring callers to test and wrap + * in [] if necessary. So JS1.3, default, and other versions all + * return an array of length 1 for uniformity. + */ + if (!IndexToId(cx, begin, &id)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) + return JS_FALSE; + } else { + if (cx->version != JSVERSION_1_2 || count > 0) { /* - * JS lacks "list context", whereby in Perl one turns the single - * scalar that's spliced out into an array just by assigning it to - * @single instead of $single, or by using it as Perl push's first - * argument, for instance. - * - * JS1.2 emulated Perl too closely and returned a non-Array for - * the single-splice-out case, requiring callers to test and wrap - * in [] if necessary. So JS1.3, default, and other versions all - * return an array of length 1 for uniformity. + * Create a new array value to return. Our ECMA v2 proposal specs + * that splice always returns an array value, even when given no + * arguments. We think this is best because it eliminates the need + * for callers to do an extra test to handle the empty splice case. */ - id = INT_TO_JSVAL(begin); - if (!js_GetProperty(cx, obj, id, rval)) { - ok = JS_FALSE; - goto out; - } - } else { obj2 = js_NewArrayObject(cx, 0, NULL); - if (!obj2) { - ok = JS_FALSE; - goto out; - } + if (!obj2) + return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj2); + } + + /* If there are elements to remove, put them into the return value. */ + if (count > 0) { for (last = begin; last < end; last++) { - id = INT_TO_JSVAL(last); - id2 = INT_TO_JSVAL(last - begin); - if (!js_GetProperty(cx, obj, id, &v) || - !js_SetProperty(cx, obj2, id2, &v)) { - ok = JS_FALSE; - goto out; - } + if (!IndexToId(cx, last, &id)) + return JS_FALSE; + if (!IndexToId(cx, last - begin, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj2, id2, &v)) + return JS_FALSE; } } } /* Find the direction (up or down) to copy and make way for argv. */ - delta = (jsint)argc - count; - if (delta > 0) { - for (last = length - 1; last >= end; last--) { - id = INT_TO_JSVAL(last); - id2 = INT_TO_JSVAL(last + delta); - prop = js_GetProperty(cx, obj, id, &v); - if (!prop || !js_SetProperty(cx, obj, id2, &v)) { - ok = JS_FALSE; - goto out; - } + if (argc > count) { + delta = (jsuint)argc - count; + last = length; + /* (uint) end could be 0, so can't use vanilla >= test */ + while (last-- > end) { + if (!IndexToId(cx, last, &id)) + return JS_FALSE; + if (!IndexToId(cx, last + delta, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; } - } else if (delta < 0) { + length += delta; + } else if (argc < count) { + delta = count - (jsuint)argc; for (last = end; last < length; last++) { - id = INT_TO_JSVAL(last); - id2 = INT_TO_JSVAL(last + delta); - prop = js_GetProperty(cx, obj, id, &v); - if (!prop || !js_SetProperty(cx, obj, id2, &v)) { - ok = JS_FALSE; - goto out; - } + if (!IndexToId(cx, last, &id)) + return JS_FALSE; + if (!IndexToId(cx, last - delta, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; } + length -= delta; } /* Copy from argv into the hole to complete the splice. */ for (i = 0; i < argc; i++) { - id = INT_TO_JSVAL(begin + i); - if (!js_SetProperty(cx, obj, id, &argv[i])) { - ok = JS_FALSE; - goto out; - } + if (!IndexToId(cx, begin + i, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) + return JS_FALSE; } /* Update length in case we deleted elements from the end. */ - if (!SetLengthProperty(cx, obj, length + delta)) - ok = JS_FALSE; -out: - JS_UNLOCK(cx); - return ok; + return js_SetLengthProperty(cx, obj, length); #else return array_nyi(cx, "splice"); #endif @@ -1000,143 +1034,132 @@ static JSBool array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *nobj, *aobj; - JSBool ok; - jsint slot, length, alength; - jsval v, id, id2, lv, *vp; - JSProperty *prop; + jsuint slot, length, alength; + jsid id, id2; + jsval v; uintN i; - nobj = JS_NewArrayObject(cx, 0, NULL); + nobj = js_NewArrayObject(cx, 0, NULL); if (!nobj) return JS_FALSE; - ok = JS_TRUE; - JS_LOCK(cx); - if (!GetLengthProperty(cx, obj, &length)) { - ok = JS_FALSE; - goto out; - } - for (slot = 0; slot < length; slot++) { - id = INT_TO_JSVAL(slot); - prop = js_GetProperty(cx, obj, id, &v); - if (!prop || !js_SetProperty(cx, nobj, id, &v)) { - ok = JS_FALSE; - goto out; - } + /* Only add the first element as an array if it looks like one. Treat the + * target the same way as the arguments. + */ + + /* XXXmccabe Might make sense to recast all of this as a do-while. */ + if (js_HasLengthProperty(cx, obj, &length)) { + for (slot = 0; slot < length; slot++) { + if (!IndexToId(cx, slot, &id)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, nobj, id, &v)) + return JS_FALSE; + } + } else { + length = 1; + v = OBJECT_TO_JSVAL(obj); + if (!OBJ_SET_PROPERTY(cx, nobj, JSVAL_ZERO, &v)) + return JS_FALSE; } for (i = 0; i < argc; i++) { v = argv[i]; if (JSVAL_IS_OBJECT(v)) { aobj = JSVAL_TO_OBJECT(v); - if (aobj && (prop = js_HasLengthProperty(cx, aobj))) { - vp = &aobj->slots[prop->slot]; - lv = *vp; - ok = prop->getter(cx, aobj, - (jsval)cx->runtime->atomState.lengthAtom, - &lv); - if (!ok) - goto out; - *vp = lv; - ok = ValueToLength(cx, lv, &alength); - if (!ok) - goto out; + if (aobj && js_HasLengthProperty(cx, aobj, &alength)) { for (slot = 0; slot < alength; slot++) { - id = INT_TO_JSVAL(slot); - id2 = INT_TO_JSVAL(length + slot); - prop = js_GetProperty(cx, aobj, id, &v); - if (!prop || !js_SetProperty(cx, nobj, id2, &v)) { - ok = JS_FALSE; - goto out; - } + if (!IndexToId(cx, slot, &id)) + return JS_FALSE; + if (!IndexToId(cx, length + slot, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, aobj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v)) + return JS_FALSE; } length += alength; continue; } } - id = INT_TO_JSVAL(length); - if (!js_SetProperty(cx, nobj, id, &v)) { - ok = JS_FALSE; - goto out; - } + if (!IndexToId(cx, length, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, nobj, id, &v)) + return JS_FALSE; length++; } *rval = OBJECT_TO_JSVAL(nobj); -out: - JS_UNLOCK(cx); - return ok; + return JS_TRUE; } static JSBool array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *nobj; - JSBool ok; - jsint length, begin, end, slot; + jsuint length, begin, end, slot; jsdouble d; - jsval v, id, id2; - JSProperty *prop; + jsid id, id2; + jsval v; - nobj = JS_NewArrayObject(cx, 0, NULL); + nobj = js_NewArrayObject(cx, 0, NULL); if (!nobj) return JS_FALSE; - ok = JS_TRUE; - JS_LOCK(cx); - if (!GetLengthProperty(cx, obj, &length)) { - ok = JS_FALSE; - goto out; - } + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; begin = 0; end = length; if (argc > 0) { - ok = js_ValueToNumber(cx, argv[0], &d); - if (!ok) - goto out; - begin = (jsint)d; - if (begin < 0) { - begin += length; - if (begin < 0) - begin = 0; - } else if (begin > length) { - begin = length; + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) { + d += length; + if (d < 0) + d = 0; + } else if (d > length) { + d = length; } + begin = (jsuint)d; if (argc > 1) { - ok = js_ValueToNumber(cx, argv[1], &d); - if (!ok) - goto out; - end = (jsint)d; - if (end < 0) { - end += length; - if (end < 0) - end = 0; - } else if (end > length) { - end = length; + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) { + d += length; + if (d < 0) + d = 0; + } else if (d > length) { + d = length; } + end = (jsuint)d; } } for (slot = begin; slot < end; slot++) { - id = INT_TO_JSVAL(slot); - id2 = INT_TO_JSVAL(slot - begin); - prop = js_GetProperty(cx, obj, id, &v); - if (!prop || !js_SetProperty(cx, nobj, id2, &v)) { - ok = JS_FALSE; - goto out; - } + if (!IndexToId(cx, slot, &id)) + return JS_FALSE; + if (!IndexToId(cx, slot - begin, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v)) + return JS_FALSE; } *rval = OBJECT_TO_JSVAL(nobj); -out: - JS_UNLOCK(cx); - return ok; + return JS_TRUE; } #endif /* JS_HAS_SEQUENCE_OPS */ static JSFunctionSpec array_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, array_toSource, 0}, +#endif {js_toString_str, array_toString, 0}, /* Perl-ish methods. */ @@ -1164,36 +1187,51 @@ static JSFunctionSpec array_methods[] = { static JSBool Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - jsint length; + jsuint length; jsval *vector; - JSBool ok; + + /* If called without new, replace obj with a new Array object. */ + if (!cx->fp->constructing) { + obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } if (argc == 0) { length = 0; vector = NULL; - } else if (cx->version < JSVERSION_1_2 && - argc == 1 && JSVAL_IS_INT(argv[0])) { - length = JSVAL_TO_INT(argv[0]); + } else if (cx->version != JSVERSION_1_2 && + argc == 1 && ValueIsLength(cx, argv[0], &length)) + { + /* + * Only use 1 arg as first element for version 1.2; for any other + * version (including 1.3), follow ECMA and use it as a length. + */ vector = NULL; } else { - length = argc; + length = (jsuint) argc; vector = argv; } - JS_LOCK_VOID(cx, ok = InitArrayObject(cx, obj, length, vector)); - if (ok) - *rval = OBJECT_TO_JSVAL(obj); - return ok; + return InitArrayObject(cx, obj, length, vector); } JSObject * js_InitArrayClass(JSContext *cx, JSObject *obj) { - return JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1, - NULL, array_methods, NULL, NULL); + JSObject *proto; + + proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1, + NULL, array_methods, NULL, NULL); + + /* Initialize the Array prototype object so it gets a length property. */ + if (!proto || !InitArrayObject(cx, proto, 0, NULL)) + return NULL; + return proto; } JSObject * -js_NewArrayObject(JSContext *cx, jsint length, jsval *vector) +js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector) { JSObject *obj; @@ -1206,32 +1244,3 @@ js_NewArrayObject(JSContext *cx, jsint length, jsval *vector) } return obj; } - -JSBool -js_GetArrayLength(JSContext *cx, JSObject *obj, jsint *lengthp) -{ - return GetLengthProperty(cx, obj, lengthp) != NULL; -} - -JSBool -js_SetArrayLength(JSContext *cx, JSObject *obj, jsint length) -{ - return SetLengthProperty(cx, obj, length) != NULL; -} - -JSProperty * -js_HasLengthProperty(JSContext *cx, JSObject *obj) -{ - JSErrorReporter older; - JSObject *pobj; - JSProperty *prop; - JSBool ok; - - PR_ASSERT(JS_IS_LOCKED(cx)); - older = JS_SetErrorReporter(cx, NULL); - pobj = NULL; - ok = js_LookupProperty(cx, obj, (jsval)cx->runtime->atomState.lengthAtom, - &pobj, &prop); - JS_SetErrorReporter(cx, older); - return (ok && prop && prop->object == obj) ? prop : NULL; -} diff --git a/mozilla/js/src/jsarray.h b/mozilla/js/src/jsarray.h index 3b00db29d12..7c8d417f218 100644 --- a/mozilla/js/src/jsarray.h +++ b/mozilla/js/src/jsarray.h @@ -32,16 +32,16 @@ extern JSObject * js_InitArrayClass(JSContext *cx, JSObject *obj); extern JSObject * -js_NewArrayObject(JSContext *cx, jsint length, jsval *vector); +js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector); extern JSBool -js_GetArrayLength(JSContext *cx, JSObject *obj, jsint *lengthp); +js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); extern JSBool -js_SetArrayLength(JSContext *cx, JSObject *obj, jsint length); +js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length); -extern JSProperty * -js_HasLengthProperty(JSContext *cx, JSObject *obj); +extern JSBool +js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); /* * JS-specific qsort function. diff --git a/mozilla/js/src/jsatom.c b/mozilla/js/src/jsatom.c index 420c65e32ca..6c909ddc879 100644 --- a/mozilla/js/src/jsatom.c +++ b/mozilla/js/src/jsatom.c @@ -51,10 +51,20 @@ char *js_type_str[] = { "boolean", }; +char *js_boolean_str[] = { + js_false_str, + js_true_str +}; + char js_Array_str[] = "Array"; +char js_Math_str[] = "Math"; char js_Object_str[] = "Object"; char js_anonymous_str[] = "anonymous"; +char js_arguments_str[] = "arguments"; +char js_arity_str[] = "arity"; char js_assign_str[] = "assign"; +char js_callee_str[] = "callee"; +char js_caller_str[] = "caller"; char js_class_prototype_str[] = "prototype"; char js_constructor_str[] = "constructor"; char js_count_str[] = "__count__"; @@ -62,8 +72,10 @@ char js_eval_str[] = "eval"; char js_index_str[] = "index"; char js_input_str[] = "input"; char js_length_str[] = "length"; +char js_name_str[] = "name"; char js_parent_str[] = "__parent__"; char js_proto_str[] = "__proto__"; +char js_toSource_str[] = "toSource"; char js_toString_str[] = "toString"; char js_valueOf_str[] = "valueOf"; @@ -135,12 +147,13 @@ js_alloc_atom(void *priv, const void *key) atom = malloc(sizeof(JSAtom)); if (!atom) return NULL; +#ifdef JS_THREADSAFE + state->tablegen++; +#endif atom->entry.key = key; atom->entry.value = NULL; - atom->nrefs = 0; atom->flags = 0; atom->kwindex = -1; - atom->index = 0; atom->number = state->number++; return &atom->entry; } @@ -148,11 +161,15 @@ js_alloc_atom(void *priv, const void *key) PR_STATIC_CALLBACK(void) js_free_atom(void *priv, PRHashEntry *he, uintN flag) { - if (flag == HT_FREE_ENTRY) - free(he); + if (flag != HT_FREE_ENTRY) + return; +#ifdef JS_THREADSAFE + ((JSAtomState *)priv)->tablegen++; +#endif + free(he); } -static PRHashAllocOps atomAllocOps = { +static PRHashAllocOps atom_alloc_ops = { js_alloc_atom_space, js_free_atom_space, js_alloc_atom, js_free_atom }; @@ -164,17 +181,22 @@ js_InitAtomState(JSContext *cx, JSAtomState *state) { uintN i; + state->runtime = cx->runtime; state->number = 0; state->table = PR_NewHashTable(JS_ATOM_HASH_SIZE, js_hash_atom_key, js_compare_atom_keys, js_compare_stub, - &atomAllocOps, state); + &atom_alloc_ops, state); if (!state->table) { JS_ReportOutOfMemory(cx); return JS_FALSE; } +#ifdef JS_THREADSAFE + js_NewLock(&state->lock); + state->tablegen = 0; +#endif #define FROB(lval,str) { \ - if (!(state->lval = js_Atomize(cx, str, strlen(str), 0))) { \ + if (!(state->lval = js_Atomize(cx, str, strlen(str), ATOM_PINNED))) { \ js_FreeAtomState(cx, state); \ return JS_FALSE; \ } \ @@ -189,17 +211,24 @@ js_InitAtomState(JSContext *cx, JSAtomState *state) FROB(nullAtom, js_null_str); FROB(ArrayAtom, js_Array_str); + FROB(MathAtom, js_Math_str); FROB(ObjectAtom, js_Object_str); FROB(anonymousAtom, js_anonymous_str); + FROB(argumentsAtom, js_arguments_str); + FROB(arityAtom, js_arity_str); FROB(assignAtom, js_assign_str); + FROB(calleeAtom, js_callee_str); + FROB(callerAtom, js_caller_str); FROB(classPrototypeAtom, js_class_prototype_str); FROB(constructorAtom, js_constructor_str); FROB(countAtom, js_count_str); FROB(indexAtom, js_index_str); FROB(inputAtom, js_input_str); FROB(lengthAtom, js_length_str); + FROB(nameAtom, js_name_str); FROB(parentAtom, js_parent_str); FROB(protoAtom, js_proto_str); + FROB(toSourceAtom, js_toSource_str); FROB(toStringAtom, js_toString_str); FROB(valueOfAtom, js_valueOf_str); @@ -208,103 +237,116 @@ js_InitAtomState(JSContext *cx, JSAtomState *state) return JS_TRUE; } -PR_STATIC_CALLBACK(intN) -js_atom_key_zapper(PRHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - - atom = (JSAtom *)he; - if (atom->nrefs == 0) - return HT_ENUMERATE_REMOVE; - he->key = NULL; - return HT_ENUMERATE_NEXT; -} - void js_FreeAtomState(JSContext *cx, JSAtomState *state) { - PRHashTable *table; - - js_ForceGC(cx); - table = state->table; - PR_HashTableEnumerateEntries(table, js_atom_key_zapper, NULL); - js_ForceGC(cx); - PR_HashTableDestroy(table); + state->runtime = NULL; + PR_HashTableDestroy(state->table); state->table = NULL; state->number = 0; +#ifdef JS_THREADSAFE + js_DestroyLock(&state->lock); +#endif } -typedef struct MarkAtomArgs { +typedef struct MarkArgs { JSRuntime *runtime; - JSAtomMarker mark; -} MarkAtomArgs; + JSGCThingMarker mark; +} MarkArgs; PR_STATIC_CALLBACK(intN) -js_atom_key_marker(PRHashEntry *he, intN i, void *arg) +js_atom_marker(PRHashEntry *he, intN i, void *arg) { JSAtom *atom; jsval key; - MarkAtomArgs *args; + MarkArgs *args; atom = (JSAtom *)he; - if (atom->nrefs == 0) { - /* - * Unreferenced atom, probably from the scanner atomizing a name, - * number, or string that the parser did not save in an atom map - * (because it was a syntax error, e.g.). - */ - return HT_ENUMERATE_REMOVE; - } - key = ATOM_KEY(atom); - if (JSVAL_IS_GCTHING(key)) { - args = arg; - args->mark(args->runtime, JSVAL_TO_GCTHING(key)); + if (atom->flags & ATOM_PINNED) { + atom->flags |= ATOM_MARK; + key = ATOM_KEY(atom); + if (JSVAL_IS_GCTHING(key)) { + args = arg; + args->mark(args->runtime, JSVAL_TO_GCTHING(key)); + } } return HT_ENUMERATE_NEXT; } void -js_MarkAtomState(JSRuntime *rt, JSAtomMarker mark) +js_MarkAtomState(JSAtomState *state, JSGCThingMarker mark) { - PRHashTable *table; - MarkAtomArgs args; + MarkArgs args; - table = rt->atomState.table; - if (!table) - return; - args.runtime = rt; + args.runtime = state->runtime; args.mark = mark; - PR_HashTableEnumerateEntries(table, js_atom_key_marker, &args); + PR_HashTableEnumerateEntries(state->table, js_atom_marker, &args); +} + +PR_STATIC_CALLBACK(intN) +js_atom_sweeper(PRHashEntry *he, intN i, void *arg) +{ + JSAtom *atom; + + atom = (JSAtom *)he; + if (atom->flags & ATOM_MARK) { + atom->flags &= ~ATOM_MARK; + return HT_ENUMERATE_NEXT; + } + PR_ASSERT((atom->flags & ATOM_PINNED) == 0); + atom->entry.key = NULL; + atom->flags = 0; + return HT_ENUMERATE_REMOVE; +} + +void +js_SweepAtomState(JSAtomState *state) +{ + PR_HashTableEnumerateEntries(state->table, js_atom_sweeper, NULL); +} + +PR_STATIC_CALLBACK(intN) +js_atom_unpinner(PRHashEntry *he, intN i, void *arg) +{ + JSAtom *atom; + + atom = (JSAtom *)he; + atom->flags &= ~ATOM_PINNED; + return HT_ENUMERATE_NEXT; +} + +void +js_UnpinPinnedAtoms(JSAtomState *state) +{ + PR_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL); } static JSAtom * js_AtomizeHashedKey(JSContext *cx, jsval key, PRHashNumber keyHash, uintN flags) { + JSAtomState *state; PRHashTable *table; PRHashEntry *he, **hep; JSAtom *atom; - PR_ASSERT(JS_IS_LOCKED(cx)); - table = cx->runtime->atomState.table; + state = &cx->runtime->atomState; + JS_LOCK(&state->lock,cx); + table = state->table; hep = PR_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { he = PR_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); - return NULL; + atom = NULL; + goto out; } } atom = (JSAtom *)he; -#ifdef DEBUG_DUPLICATE_ATOMS - hep = PR_HashTableRawLookup(table, keyHash, (void *)key); - he = *hep; - PR_ASSERT(atom == (JSAtom *)he); -#endif - - if (flags & ATOM_NOHOLD) - return atom; - return js_HoldAtom(cx, atom); + atom->flags |= flags; +out: + JS_UNLOCK(&state->lock,cx); + return atom; } JSAtom * @@ -345,9 +387,10 @@ JSAtom * js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags) { jsdouble *dp; - PRHashTable *table; PRHashNumber keyHash; jsval key; + JSAtomState *state; + PRHashTable *table; PRHashEntry *he, **hep; JSAtom *atom; @@ -360,74 +403,100 @@ js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags) *dp = d; #endif - PR_ASSERT(JS_IS_LOCKED(cx)); - table = cx->runtime->atomState.table; keyHash = HASH_DOUBLE(dp); key = DOUBLE_TO_JSVAL(dp); + state = &cx->runtime->atomState; + JS_LOCK(&state->lock,cx); + table = state->table; hep = PR_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { +#ifdef JS_THREADSAFE + uint32 gen = state->tablegen; +#endif + JS_UNLOCK(&state->lock,cx); if (!js_NewDoubleValue(cx, d, &key)) return NULL; + JS_LOCK(&state->lock,cx); +#ifdef JS_THREADSAFE + if (state->tablegen != gen) { + hep = PR_HashTableRawLookup(table, keyHash, (void *)key); + if ((he = *hep) != NULL) { + atom = (JSAtom *)he; + goto out; + } + } +#endif he = PR_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); - return NULL; + atom = NULL; + goto out; } } atom = (JSAtom *)he; -#ifdef DEBUG_DUPLICATE_ATOMS - hep = PR_HashTableRawLookup(table, keyHash, (void *)key); - he = *hep; - PR_ASSERT(atom == (JSAtom *)he); -#endif - - if (flags & ATOM_NOHOLD) - return atom; - return js_HoldAtom(cx, atom); + atom->flags |= flags; +out: + JS_UNLOCK(&state->lock,cx); + return atom; } JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags) { - PRHashTable *table; PRHashNumber keyHash; jsval key; + JSAtomState *state; + PRHashTable *table; PRHashEntry *he, **hep; JSAtom *atom; - PR_ASSERT(JS_IS_LOCKED(cx)); - table = cx->runtime->atomState.table; keyHash = js_HashString(str); key = STRING_TO_JSVAL(str); + state = &cx->runtime->atomState; + JS_LOCK(&state->lock,cx); + table = state->table; hep = PR_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { if (flags & ATOM_TMPSTR) { - if (flags & ATOM_NOCOPY) +#ifdef JS_THREADSAFE + uint32 gen = state->tablegen; +#endif + JS_UNLOCK(&state->lock,cx); + flags &= ~ATOM_TMPSTR; + if (flags & ATOM_NOCOPY) { + flags &= ~ATOM_NOCOPY; str = js_NewString(cx, str->chars, str->length, 0); - else + } else { str = js_NewStringCopyN(cx, str->chars, str->length, 0); + } if (!str) return NULL; key = STRING_TO_JSVAL(str); + JS_LOCK(&state->lock,cx); +#ifdef JS_THREADSAFE + if (state->tablegen != gen) { + hep = PR_HashTableRawLookup(table, keyHash, (void *)key); + if ((he = *hep) != NULL) { + atom = (JSAtom *)he; + goto out; + } + } +#endif } he = PR_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); - return NULL; + atom = NULL; + goto out; } } atom = (JSAtom *)he; -#ifdef DEBUG_DUPLICATE_ATOMS - hep = PR_HashTableRawLookup(table, keyHash, (void *)key); - he = *hep; - PR_ASSERT(atom == (JSAtom *)he); -#endif - - if (flags & ATOM_NOHOLD) - return atom; - return js_HoldAtom(cx, atom); + atom->flags |= flags; +out: + JS_UNLOCK(&state->lock,cx); + return atom; } JS_FRIEND_API(JSAtom *) @@ -503,72 +572,27 @@ js_ValueToStringAtom(JSContext *cx, jsval v) return js_AtomizeString(cx, str, 0); } -jsatomid +JSAtomListElement * js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al) { - if (!(atom->flags & ATOM_INDEXED)) { - atom->flags |= ATOM_INDEXED; - atom->index = (jsatomid) al->count++; - ATOM_SET_NEXT(atom, al->list); - al->list = js_HoldAtom(cx, atom); + JSAtomListElement *ale; + + ATOM_LIST_SEARCH(ale, al, atom); + if (!ale) { + PR_ARENA_ALLOCATE(ale, &cx->tempPool, sizeof(JSAtomListElement)); + if (!ale) { + JS_ReportOutOfMemory(cx); + return NULL; + } + ale->atom = atom; + ale->index = (jsatomid) al->count++; + ale->next = al->list; + al->list = ale; } - return atom->index; -} - -JSAtom * -js_HoldAtom(JSContext *cx, JSAtom *atom) -{ -#ifdef DEBUG_DUPLICATE_ATOMS - jsval key; - PRHashNumber keyHash; - PRHashEntry *he, **hep; - - key = ATOM_KEY(atom); - keyHash = js_hash_atom_key((void *)key); - hep = PR_HashTableRawLookup(cx->runtime->atomState.table, keyHash, - (void*)key); - he = *hep; - PR_ASSERT(atom == (JSAtom *)he); -#endif - PR_ASSERT(JS_IS_LOCKED(cx)); - atom->nrefs++; - PR_ASSERT(atom->nrefs > 0); - return atom; + return ale; } JS_FRIEND_API(JSAtom *) -js_DropAtom(JSContext *cx, JSAtom *atom) -{ -#ifdef DEBUG_DUPLICATE_ATOMS - jsval key; - PRHashNumber keyHash; - PRHashEntry *he, **hep; - - key = ATOM_KEY(atom); - keyHash = js_hash_atom_key((void *)key); - hep = PR_HashTableRawLookup(cx->runtime->atomState.table, keyHash, - (void*)key); - he = *hep; - PR_ASSERT(atom == (JSAtom *)he); -#endif - PR_ASSERT(JS_IS_LOCKED(cx)); - PR_ASSERT(atom->nrefs > 0); - if (atom->nrefs <= 0) - return NULL; - if (--atom->nrefs == 0) { - PR_HashTableRemove(cx->runtime->atomState.table, atom->entry.key); -#ifdef DEBUG_DUPLICATE_ATOMS - hep = PR_HashTableRawLookup(cx->runtime->atomState.table, keyHash, - (void *)key); - he = *hep; - PR_ASSERT(he == NULL); -#endif - atom = NULL; - } - return atom; -} - -JSAtom * js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i) { JSAtom *atom; @@ -586,11 +610,12 @@ js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i) JS_FRIEND_API(JSBool) js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) { - JSAtom *atom, *next, **vector; + JSAtom **vector; + JSAtomListElement *ale, *next; uint32 count; - atom = al->list; - if (!atom) { + ale = al->list; + if (!ale) { map->vector = NULL; map->length = 0; return JS_TRUE; @@ -606,11 +631,10 @@ js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) return JS_FALSE; do { - vector[atom->index] = atom; - atom->flags &= ~ATOM_INDEXED; - next = ATOM_NEXT(atom); - ATOM_SET_NEXT(atom, NULL); - } while ((atom = next) != NULL); + vector[ale->index] = ale->atom; + next = ale->next; + ale->next = NULL; + } while ((ale = next) != NULL); al->list = NULL; al->count = 0; @@ -622,28 +646,9 @@ js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) JS_FRIEND_API(void) js_FreeAtomMap(JSContext *cx, JSAtomMap *map) { - uintN i; - if (map->vector) { - for (i = 0; i < map->length; i++) - js_DropAtom(cx, map->vector[i]); free(map->vector); map->vector = NULL; } map->length = 0; } - -void -js_DropUnmappedAtoms(JSContext *cx, JSAtomList *al) -{ - JSAtom *atom, *next; - - for (atom = al->list; atom; atom = next) { - atom->flags &= ~ATOM_INDEXED; - next = ATOM_NEXT(atom); - ATOM_SET_NEXT(atom, NULL); - js_DropAtom(cx, atom); - } - al->list = NULL; - al->count = 0; -} diff --git a/mozilla/js/src/jsatom.h b/mozilla/js/src/jsatom.h index 4b615de0e26..102909ba520 100644 --- a/mozilla/js/src/jsatom.h +++ b/mozilla/js/src/jsatom.h @@ -32,19 +32,21 @@ #include "jsprvtd.h" #include "jspubtd.h" +#ifdef JS_THREADSAFE +#include "jslock.h" +#endif + PR_BEGIN_EXTERN_C -#define ATOM_INDEXED 0x01 /* indexed for literal mapping */ -#define ATOM_NOCOPY 0x02 /* don't copy atom string bytes */ -#define ATOM_NOHOLD 0x04 /* atomize a weak link for parser */ -#define ATOM_TMPSTR 0x08 /* internal, to avoid extra string */ +#define ATOM_NOCOPY 0x01 /* don't copy atom string bytes */ +#define ATOM_TMPSTR 0x02 /* internal, to avoid extra string */ +#define ATOM_MARK 0x04 /* atom is reachable via GC */ +#define ATOM_PINNED 0x08 /* atom is pinned against GC */ struct JSAtom { PRHashEntry entry; /* key is jsval, value keyword info */ - jsrefcount nrefs; /* reference count (not at front!) */ - uint8 flags; /* flags, ATOM_INDEXED or 0 for now */ + uint8 flags; /* flags, PINNED and/or MARK for now */ int8 kwindex; /* keyword index, -1 if not keyword */ - jsatomid index; /* index in script-specific atom map */ jsatomid number; /* atom serial number and hash code */ }; @@ -60,15 +62,34 @@ struct JSAtom { #define ATOM_IS_BOOLEAN(atom) JSVAL_IS_BOOLEAN(ATOM_KEY(atom)) #define ATOM_TO_BOOLEAN(atom) JSVAL_TO_BOOLEAN(ATOM_KEY(atom)) #define ATOM_BYTES(atom) JS_GetStringBytes(ATOM_TO_STRING(atom)) -#define ATOM_NEXT(atom) ((JSAtom *)(atom)->entry.value) -#define ATOM_SET_NEXT(atom,next) ((atom)->entry.value = (next)) + +struct JSAtomListElement { + JSAtomListElement *next; + jsatomid index; /* index in script-specific atom map */ + JSAtom *atom; +}; struct JSAtomList { - JSAtom *list; /* literals indexed for mapping */ + JSAtomListElement *list; /* literals indexed for mapping */ jsuint count; /* count of indexed literals */ }; -#define INIT_ATOM_LIST(al) ((al)->list = NULL, (al)->count = 0) +#define ATOM_LIST_INIT(al) ((al)->list = NULL, (al)->count = 0) + +#define ATOM_LIST_SEARCH(_ale,_al,_atom) \ + PR_BEGIN_MACRO \ + JSAtomListElement **_alep = &(_al)->list; \ + while ((_ale = *_alep) != NULL) { \ + if (_ale->atom == (_atom)) { \ + /* Hit, move atom's element to the front of the list. */ \ + *_alep = _ale->next; \ + _ale->next = (_al)->list; \ + (_al)->list = _ale; \ + break; \ + } \ + _alep = &_ale->next; \ + } \ + PR_END_MACRO struct JSAtomMap { JSAtom **vector; /* array of ptrs to indexed atoms */ @@ -76,6 +97,7 @@ struct JSAtomMap { }; struct JSAtomState { + JSRuntime *runtime; /* runtime that owns us */ PRHashTable *table; /* hash table containing all atoms */ jsatomid number; /* one beyond greatest atom number */ @@ -86,28 +108,46 @@ struct JSAtomState { /* Various built-in or commonly-used atoms. */ JSAtom *ArrayAtom; + JSAtom *MathAtom; JSAtom *ObjectAtom; JSAtom *anonymousAtom; + JSAtom *argumentsAtom; + JSAtom *arityAtom; JSAtom *assignAtom; + JSAtom *calleeAtom; + JSAtom *callerAtom; JSAtom *classPrototypeAtom; JSAtom *constructorAtom; JSAtom *countAtom; JSAtom *indexAtom; JSAtom *inputAtom; JSAtom *lengthAtom; + JSAtom *nameAtom; JSAtom *parentAtom; JSAtom *protoAtom; + JSAtom *toSourceAtom; JSAtom *toStringAtom; JSAtom *valueOfAtom; + +#ifdef JS_THREADSAFE + JSThinLock lock; + volatile uint32 tablegen; +#endif }; /* Well-known predefined strings and their atoms. */ extern char *js_type_str[]; +extern char *js_boolean_str[]; extern char js_Array_str[]; +extern char js_Math_str[]; extern char js_Object_str[]; extern char js_anonymous_str[]; +extern char js_arguments_str[]; +extern char js_arity_str[]; extern char js_assign_str[]; +extern char js_callee_str[]; +extern char js_caller_str[]; extern char js_class_prototype_str[]; extern char js_constructor_str[]; extern char js_count_str[]; @@ -115,32 +155,40 @@ extern char js_eval_str[]; extern char js_index_str[]; extern char js_input_str[]; extern char js_length_str[]; +extern char js_name_str[]; extern char js_parent_str[]; extern char js_proto_str[]; +extern char js_toSource_str[]; extern char js_toString_str[]; extern char js_valueOf_str[]; /* - * Initialize atom bookkeeping. Return true on success, false with an out of + * Initialize atom state. Return true on success, false with an out of * memory error report on failure. */ extern JSBool js_InitAtomState(JSContext *cx, JSAtomState *state); /* - * Free and clear atom bookkeeping. + * Free and clear atom state. */ extern void js_FreeAtomState(JSContext *cx, JSAtomState *state); /* - * Free and clear atom bookkeeping. + * Atom garbage collection hooks. */ typedef void -(*JSAtomMarker)(JSRuntime *rt, void *thing); +(*JSGCThingMarker)(JSRuntime *rt, void *thing); extern void -js_MarkAtomState(JSRuntime *rt, JSAtomMarker mark); +js_MarkAtomState(JSAtomState *state, JSGCThingMarker mark); + +extern void +js_SweepAtomState(JSAtomState *state); + +extern void +js_UnpinPinnedAtoms(JSAtomState *state); /* * Find or create the atom for an object. If we create a new atom, give it the @@ -198,43 +246,31 @@ js_ValueToStringAtom(JSContext *cx, jsval v); /* * Assign atom an index and insert it on al. */ -extern jsatomid +extern JSAtomListElement * js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al); -/* - * Atom reference counting operators. - */ -extern JSAtom * -js_HoldAtom(JSContext *cx, JSAtom *atom); - -extern JS_FRIEND_API(JSAtom *) -js_DropAtom(JSContext *cx, JSAtom *atom); - /* * Get the atom with index i from map. */ -extern JSAtom * +extern JS_FRIEND_API(JSAtom *) js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i); /* * For all unmapped atoms recorded in al, add a mapping from the atom's index - * to its address, which holds a reference. + * to its address. The GC must not run until all indexed atoms in atomLists + * have been mapped by scripts connected to live objects (Function and Script + * class objects have scripts as/in their private data -- the GC knows about + * these two classes). */ extern JS_FRIEND_API(JSBool) js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al); /* - * Drop all atom references from map and free its vector. + * Free map->vector and clear map. */ extern JS_FRIEND_API(void) js_FreeAtomMap(JSContext *cx, JSAtomMap *map); -/* - * Drop any unmapped atoms on al. - */ -extern void -js_DropUnmappedAtoms(JSContext *cx, JSAtomList *al); - PR_END_EXTERN_C #endif /* jsatom_h___ */ diff --git a/mozilla/js/src/jsbool.c b/mozilla/js/src/jsbool.c index 591bfb664d1..fb59de4eebb 100644 --- a/mozilla/js/src/jsbool.c +++ b/mozilla/js/src/jsbool.c @@ -20,10 +20,13 @@ * JS boolean implementation. */ #include "prtypes.h" +#include "prlog.h" #include "jsapi.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" +#include "jsconfig.h" +#include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" @@ -36,6 +39,33 @@ static JSClass boolean_class = { JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; +#if JS_HAS_TOSOURCE +#include "prprf.h" + +static JSBool +bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval v; + char buf[32]; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_BOOLEAN(v)) + return js_obj_toSource(cx, obj, argc, argv, rval); + PR_snprintf(buf, sizeof buf, "(new %s(%s))", + boolean_class.name, + js_boolean_str[JSVAL_TO_BOOLEAN(v) ? 1 : 0]); + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + static JSBool bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) @@ -46,7 +76,9 @@ bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) return JS_FALSE; - JS_LOCK_VOID(cx, v = js_GetSlot(cx, obj, JSSLOT_PRIVATE)); + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_BOOLEAN(v)) + return js_obj_toString(cx, obj, argc, argv, rval); atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0]; str = ATOM_TO_STRING(atom); if (!str) @@ -60,11 +92,14 @@ bool_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) return JS_FALSE; - JS_LOCK_VOID(cx, *rval = js_GetSlot(cx, obj, JSSLOT_PRIVATE)); + *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; } static JSFunctionSpec boolean_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, bool_toSource, 0}, +#endif {js_toString_str, bool_toString, 0}, {js_valueOf_str, bool_valueOf, 0}, {0} @@ -82,19 +117,17 @@ Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jsval bval; if (argc != 0) { - if (!JS_ValueToBoolean(cx, argv[0], &b)) + if (!js_ValueToBoolean(cx, argv[0], &b)) return JS_FALSE; bval = BOOLEAN_TO_JSVAL(b); } else { bval = JSVAL_FALSE; } - if (obj->map->clasp != &boolean_class) { + if (!cx->fp->constructing) { *rval = bval; return JS_TRUE; } - if (!js_SetSlot(cx, obj, JSSLOT_PRIVATE, bval)) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, bval); return JS_TRUE; } @@ -105,8 +138,9 @@ js_InitBooleanClass(JSContext *cx, JSObject *obj) proto = JS_InitClass(cx, obj, NULL, &boolean_class, Boolean, 1, NULL, boolean_methods, NULL, NULL); - if (!proto || !js_SetSlot(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE)) + if (!proto) return NULL; + OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE); return proto; } @@ -118,10 +152,7 @@ js_BooleanToObject(JSContext *cx, JSBool b) obj = js_NewObject(cx, &boolean_class, NULL, NULL); if (!obj) return NULL; - if (!js_SetSlot(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b))) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b)); return obj; } @@ -135,7 +166,6 @@ JSBool js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) { JSBool b; - JSObject *obj; jsdouble d; #if defined XP_PC && defined _MSC_VER &&_MSC_VER <= 800 @@ -144,19 +174,22 @@ js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) return JS_TRUE; #endif - /* XXX this should be an if-else chain, but MSVC1.5 really sucks. */ + /* XXX this should be an if-else chain, but MSVC1.5 crashes if it is. */ if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { /* Must return early to avoid falling thru to JSVAL_IS_OBJECT case. */ *bp = JS_FALSE; return JS_TRUE; } if (JSVAL_IS_OBJECT(v)) { - obj = JSVAL_TO_OBJECT(v); - if (!obj->map->clasp->convert(cx, obj, JSTYPE_BOOLEAN, &v)) - return JS_FALSE; - if (!JSVAL_IS_BOOLEAN(v)) - v = JSVAL_TRUE; /* non-null object is true */ - b = JSVAL_TO_BOOLEAN(v); + if (cx->version == JSVERSION_1_2) { + if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_BOOLEAN, &v)) + return JS_FALSE; + if (!JSVAL_IS_BOOLEAN(v)) + v = JSVAL_TRUE; /* non-null object is true */ + b = JSVAL_TO_BOOLEAN(v); + } else { + b = JS_TRUE; + } } if (JSVAL_IS_STRING(v)) { b = JSVAL_TO_STRING(v)->length ? JS_TRUE : JS_FALSE; diff --git a/mozilla/js/src/jscntxt.c b/mozilla/js/src/jscntxt.c index 12307581d2a..527d9430a30 100644 --- a/mozilla/js/src/jscntxt.c +++ b/mozilla/js/src/jscntxt.c @@ -42,7 +42,7 @@ #include "jsscan.h" #include "jsscript.h" -JSInterpreterHooks *js_InterpreterHooks = 0; +JSInterpreterHooks *js_InterpreterHooks = NULL; JS_FRIEND_API(void) js_SetInterpreterHooks(JSInterpreterHooks *hooks) @@ -61,6 +61,9 @@ js_NewContext(JSRuntime *rt, size_t stacksize) memset(cx, 0, sizeof *cx); cx->runtime = rt; +#ifdef JS_THREADSAFE + js_InitContextForLocking(cx); +#endif if (rt->contextList.next == (PRCList *)&rt->contextList) { /* First context on this runtime: initialize atoms and keywords. */ if (!js_InitAtomState(cx, &rt->atomState) || @@ -69,8 +72,9 @@ js_NewContext(JSRuntime *rt, size_t stacksize) return NULL; } } + /* Atomicly append cx to rt's context list. */ + JS_LOCK_RUNTIME_VOID(rt, PR_APPEND_LINK(&cx->links, &rt->contextList)); - PR_APPEND_LINK(&cx->links, &rt->contextList); cx->version = JSVERSION_DEFAULT; cx->jsop_eq = JSOP_EQ; cx->jsop_ne = JSOP_NE; @@ -81,7 +85,7 @@ js_NewContext(JSRuntime *rt, size_t stacksize) #if JS_HAS_REGEXPS if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) { js_DestroyContext(cx); - return 0; + return NULL; } #endif return cx; @@ -94,11 +98,12 @@ js_DestroyContext(JSContext *cx) JSBool rtempty; rt = cx->runtime; - PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); /* Remove cx from context list first. */ + JS_LOCK_RUNTIME(rt); PR_REMOVE_LINK(&cx->links); rtempty = (rt->contextList.next == (PRCList *)&rt->contextList); + JS_UNLOCK_RUNTIME(rt); if (js_InterpreterHooks && js_InterpreterHooks->destroyContext) { /* This is a stub, but in case it removes roots, call it now. */ @@ -106,7 +111,25 @@ js_DestroyContext(JSContext *cx) } if (rtempty) { - /* No more contexts: clear debugging state to remove GC roots. */ + /* Unpin all pinned atoms before final GC. */ + js_UnpinPinnedAtoms(&rt->atomState); + + /* Unlock GC things held by runtime pointers. */ + js_UnlockGCThing(cx, rt->jsNaN); + js_UnlockGCThing(cx, rt->jsNegativeInfinity); + js_UnlockGCThing(cx, rt->jsPositiveInfinity); + js_UnlockGCThing(cx, rt->emptyString); + + /* + * Clear these so they get recreated if the standard classes are + * initialized again. + */ + rt->jsNaN = NULL; + rt->jsNegativeInfinity = NULL; + rt->jsPositiveInfinity = NULL; + rt->emptyString = NULL; + + /* Clear debugging state to remove GC roots. */ JS_ClearAllTraps(cx); JS_ClearAllWatchPoints(cx); } @@ -136,12 +159,14 @@ js_ContextIterator(JSRuntime *rt, JSContext **iterp) { JSContext *cx = *iterp; - PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); + JS_LOCK_RUNTIME(rt); if (!cx) cx = (JSContext *)rt->contextList.next; if ((void *)cx == &rt->contextList) - return NULL; - *iterp = (JSContext *)cx->links.next; + cx = NULL; + else + *iterp = (JSContext *)cx->links.next; + JS_UNLOCK_RUNTIME(rt); return cx; } diff --git a/mozilla/js/src/jscntxt.h b/mozilla/js/src/jscntxt.h index 2d6a55aef55..a024fedf6a0 100644 --- a/mozilla/js/src/jscntxt.h +++ b/mozilla/js/src/jscntxt.h @@ -50,6 +50,7 @@ struct JSRuntime { uint32 gcLevel; uint32 gcNumber; JSBool gcPoke; + JSGCCallback gcCallback; #ifdef JS_GCMETER JSGCStats gcStats; #endif @@ -59,10 +60,10 @@ struct JSRuntime { /* Random number generator state, used by jsmath.c. */ JSBool rngInitialized; - int64 rngMultiplier; - int64 rngAddend; - int64 rngMask; - int64 rngSeed; + PRInt64 rngMultiplier; + PRInt64 rngAddend; + PRInt64 rngMask; + PRInt64 rngSeed; jsdouble rngDscale; /* Well-known numbers held for use by this runtime's contexts. */ @@ -73,27 +74,34 @@ struct JSRuntime { /* Empty string held for use by this runtime's contexts. */ JSString *emptyString; - /* Weak links to properties, indexed by quickened get/set opcodes. */ - JSPropertyCache propertyCache; - /* List of active contexts sharing this runtime. */ PRCList contextList; - /* These are used for debugging and declared in jsdbgapi.h. */ - void *interruptHandler; + /* These are used for debugging -- see jsprvtd.h and jsdbgapi.h. */ + JSTrapHandler interruptHandler; void *interruptHandlerData; - void *newScriptHookProc; - void *newScriptHookProcData; - void *destroyScriptHookProc; - void *destroyScriptHookProcData; + JSNewScriptHook newScriptHook; + void *newScriptHookData; + JSDestroyScriptHook destroyScriptHook; + void *destroyScriptHookData; /* More debugging state, see jsdbgapi.c. */ PRCList trapList; PRCList watchPointList; + /* Weak links to properties, indexed by quickened get/set opcodes. */ + /* XXX must come after PRCLists or MSVC alignment bug bites empty lists */ + JSPropertyCache propertyCache; + #ifdef JS_THREADSAFE - /* Hook for js_lock_runtime/js_unlock_runtime/js_is_runtime_locked. */ - void *lockData; + /* These combine to interlock the GC and new requests. */ + PRLock *gcLock; + PRCondVar *gcDone; + PRCondVar *requestDone; + uint32 requestCount; + + /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */ + JSThinLock rtLock; #endif }; @@ -141,16 +149,17 @@ struct JSContext { JSBranchCallback branchCallback; JSErrorReporter errorReporter; + /* Client opaque pointer */ + void *pvt; + /* Java environment and JS errors to throw as exceptions. */ void *javaEnv; void *savedErrors; -#ifdef NETSCAPE_INTERNAL - /* I18n multibyte character scanning and transcoding support. */ - const char *charSetName; - size_t charSetNameLength; - int charSetId; - JSCharScanner charScanner; +#ifdef JS_THREADSAFE + prword thread; + JSPackedBool gcActive; + jsrefcount requestDepth; #endif }; diff --git a/mozilla/js/src/jscompat.h b/mozilla/js/src/jscompat.h index 1222e91685b..3fe5b64117b 100644 --- a/mozilla/js/src/jscompat.h +++ b/mozilla/js/src/jscompat.h @@ -1,19 +1,5 @@ -/* -*- 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 - * compliance with the NPL. You may obtain a copy of the NPL at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the NPL is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL - * for the specific language governing rights and limitations under the - * NPL. - * - * The Initial Developer of this code under the NPL is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All Rights - * Reserved. +/* -*- Mode: C; tab-width: 8 -*- + * Copyright © 1996 Netscape Communications Corporation, All Rights Reserved. */ #ifndef jscompat_h___ #define jscompat_h___ diff --git a/mozilla/js/src/jsconfig.h b/mozilla/js/src/jsconfig.h index 662fa1db488..6f55833ebf2 100644 --- a/mozilla/js/src/jsconfig.h +++ b/mozilla/js/src/jsconfig.h @@ -20,148 +20,188 @@ * JS configuration macros. */ #ifndef JS_VERSION -#define JS_VERSION 13 +#define JS_VERSION 130 #endif -#if JS_VERSION == 10 +#if JS_VERSION == 100 -#define JS_BUG_AUTO_INDEX_PROPS 1 /* new object o: o.p = v sets o[0]? */ -#define JS_BUG_NULL_INDEX_PROPS 1 /* o[0] defaults to null, not void? */ -#define JS_BUG_EMPTY_INDEX_ZERO 1 /* o[""] is equivalent to o[0]? */ -#define JS_BUG_SHORT_CIRCUIT 1 /* 1 && 1 => true, 1 && 0 => 0 bug? */ -#define JS_BUG_EAGER_TOSTRING 1 /* o.toString() trumps o.valueOf()? */ -#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0"? */ +#define JS_BUG_AUTO_INDEX_PROPS 1 /* new object o: o.p = v sets o[0] */ +#define JS_BUG_NULL_INDEX_PROPS 1 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 1 /* o[""] is equivalent to o[0] */ +#define JS_BUG_SHORT_CIRCUIT 1 /* 1 && 1 => true, 1 && 0 => 0 bug */ +#define JS_BUG_EAGER_TOSTRING 1 /* 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 1 /* fallible/intransitive equality ops */ #define JS_BUG_FALLIBLE_TONUM 1 /* fallible ValueToNumber primitive */ -#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f? */ +#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f */ +#define JS_BUG_SET_ENUMERATE 1 /* o.p=q flags o.p JSPROP_ENUMERATE */ -#define JS_HAS_PROP_DELETE 0 /* delete o.p removes p from o? */ -#define JS_HAS_CALL_OBJECT 0 /* fun.caller is stack frame obj? */ -#define JS_HAS_LABEL_STATEMENT 0 /* have break/continue to label:? */ -#define JS_HAS_DO_WHILE_LOOP 0 /* have do {...} while (b)? */ -#define JS_HAS_SWITCH_STATEMENT 0 /* have switch (v) {case c: ...}? */ -#define JS_HAS_SOME_PERL_FUN 0 /* have array.join/reverse/sort? */ -#define JS_HAS_MORE_PERL_FUN 0 /* have array.push, str.substr, etc? */ +#define JS_HAS_PROP_DELETE 0 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 0 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 0 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 0 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 0 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 0 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 0 /* has array.push, str.substr, etc */ #define JS_HAS_VALUEOF_HINT 0 /* valueOf(hint) where hint is typeof */ #define JS_HAS_LEXICAL_CLOSURE 0 /* nested functions, lexically closed */ -#define JS_HAS_APPLY_FUNCTION 0 /* have apply(fun, arg1, ... argN)? */ -#define JS_HAS_OBJ_PROTO_PROP 0 /* have o.__proto__ etc.? */ -#define JS_HAS_REGEXPS 0 /* have perl r.e.s via RegExp, /pat/ */ -#define JS_HAS_SEQUENCE_OPS 0 /* have array.slice, string.concat? */ -#define JS_HAS_OBJECT_LITERAL 0 /* have var o = {'foo': 42, 'bar':3}? */ -#define JS_HAS_OBJ_WATCHPOINT 0 /* have o.watch and o.unwatch? */ -#define JS_HAS_EXPORT_IMPORT 0 /* have export fun; import obj.fun? */ +#define JS_HAS_APPLY_FUNCTION 0 /* has fun.apply(obj, argArray) */ +#define JS_HAS_CALL_FUNCTION 0 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 0 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 0 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 0 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ #define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ -#define JS_HAS_TRIPLE_EQOPS 0 /* have === and !== identity eqops? */ -#define JS_HAS_SHARP_VARS 0 /* have #n=, #n# for object literals */ -#define JS_HAS_REPLACE_LAMBDA 0 /* have string.replace(re, lambda)? */ +#define JS_HAS_TRIPLE_EQOPS 0 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 0 /* has string.replace(re, lambda) */ +#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 0 /* has XDR API and object methods */ +#define JS_HAS_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 0 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#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 */ -#elif JS_VERSION == 11 +#elif JS_VERSION == 110 -#define JS_BUG_AUTO_INDEX_PROPS 0 /* new object o: o.p = v sets o[0]? */ -#define JS_BUG_NULL_INDEX_PROPS 1 /* o[0] defaults to null, not void? */ -#define JS_BUG_EMPTY_INDEX_ZERO 1 /* o[""] is equivalent to o[0]? */ -#define JS_BUG_SHORT_CIRCUIT 1 /* 1 && 1 => true, 1 && 0 => 0 bug? */ -#define JS_BUG_EAGER_TOSTRING 1 /* o.toString() trumps o.valueOf()? */ -#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0"? */ +#define JS_BUG_AUTO_INDEX_PROPS 0 /* new object o: o.p = v sets o[0] */ +#define JS_BUG_NULL_INDEX_PROPS 1 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 1 /* o[""] is equivalent to o[0] */ +#define JS_BUG_SHORT_CIRCUIT 1 /* 1 && 1 => true, 1 && 0 => 0 bug */ +#define JS_BUG_EAGER_TOSTRING 1 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ #define JS_BUG_EVAL_THIS_FUN 1 /* eval('this') in function f is f */ #define JS_BUG_EVAL_THIS_SCOPE 1 /* Math.eval('sin(x)') vs. local x */ #define JS_BUG_FALLIBLE_EQOPS 1 /* fallible/intransitive equality ops */ #define JS_BUG_FALLIBLE_TONUM 1 /* fallible ValueToNumber primitive */ -#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f? */ +#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f */ +#define JS_BUG_SET_ENUMERATE 1 /* o.p=q flags o.p JSPROP_ENUMERATE */ -#define JS_HAS_PROP_DELETE 0 /* delete o.p removes p from o? */ -#define JS_HAS_CALL_OBJECT 0 /* fun.caller is stack frame obj? */ -#define JS_HAS_LABEL_STATEMENT 0 /* have break/continue to label:? */ -#define JS_HAS_DO_WHILE_LOOP 0 /* have do {...} while (b)? */ -#define JS_HAS_SWITCH_STATEMENT 0 /* have switch (v) {case c: ...}? */ -#define JS_HAS_SOME_PERL_FUN 1 /* have array.join/reverse/sort? */ -#define JS_HAS_MORE_PERL_FUN 0 /* have array.push, str.substr, etc? */ +#define JS_HAS_PROP_DELETE 0 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 0 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 0 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 0 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 0 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 0 /* has array.push, str.substr, etc */ #define JS_HAS_VALUEOF_HINT 0 /* valueOf(hint) where hint is typeof */ #define JS_HAS_LEXICAL_CLOSURE 0 /* nested functions, lexically closed */ -#define JS_HAS_APPLY_FUNCTION 0 /* have apply(fun, arg1, ... argN)? */ -#define JS_HAS_OBJ_PROTO_PROP 0 /* have o.__proto__ etc.? */ -#define JS_HAS_REGEXPS 0 /* have perl r.e.s via RegExp, /pat/ */ -#define JS_HAS_SEQUENCE_OPS 0 /* have array.slice, string.concat? */ -#define JS_HAS_OBJECT_LITERAL 0 /* have var o = {'foo': 42, 'bar':3}? */ -#define JS_HAS_OBJ_WATCHPOINT 0 /* have o.watch and o.unwatch? */ -#define JS_HAS_EXPORT_IMPORT 0 /* have export fun; import obj.fun? */ +#define JS_HAS_APPLY_FUNCTION 0 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 0 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 0 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 0 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 0 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ #define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ -#define JS_HAS_TRIPLE_EQOPS 0 /* have === and !== identity eqops? */ -#define JS_HAS_SHARP_VARS 0 /* have #n=, #n# for object literals */ -#define JS_HAS_REPLACE_LAMBDA 0 /* have string.replace(re, lambda)? */ +#define JS_HAS_TRIPLE_EQOPS 0 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 0 /* has string.replace(re, lambda) */ +#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 0 /* has XDR API and object methods */ +#define JS_HAS_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 0 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#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 */ -#elif JS_VERSION == 12 +#elif JS_VERSION == 120 -#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 1 /* void 0 + 0 == "undefined0"? */ +#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 1 /* 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_WITH_CLOSURE 1 /* with(o)function f(){} sets o.f */ +#define JS_BUG_SET_ENUMERATE 1 /* 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 /* have break/continue to label:? */ -#define JS_HAS_DO_WHILE_LOOP 1 /* have do {...} while (b)? */ -#define JS_HAS_SWITCH_STATEMENT 1 /* have switch (v) {case c: ...}? */ -#define JS_HAS_SOME_PERL_FUN 1 /* have array.join/reverse/sort? */ -#define JS_HAS_MORE_PERL_FUN 1 /* have array.push, str.substr, etc? */ +#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 /* have apply(fun, arg1, ... argN)? */ -#define JS_HAS_OBJ_PROTO_PROP 1 /* have o.__proto__ etc.? */ -#define JS_HAS_REGEXPS 1 /* have perl r.e.s via RegExp, /pat/ */ -#define JS_HAS_SEQUENCE_OPS 1 /* have array.slice, string.concat? */ -#define JS_HAS_OBJECT_LITERAL 1 /* have var o = {'foo': 42, 'bar':3}? */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* have o.watch and o.unwatch? */ -#define JS_HAS_EXPORT_IMPORT 1 /* have export fun; import obj.fun? */ +#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 0 /* 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 0 /* have === and !== identity eqops? */ -#define JS_HAS_SHARP_VARS 0 /* have #n=, #n# for object literals */ -#define JS_HAS_REPLACE_LAMBDA 0 /* have string.replace(re, lambda)? */ +#define JS_HAS_TRIPLE_EQOPS 0 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 0 /* has string.replace(re, lambda) */ +#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 0 /* has XDR API and object methods */ +#define JS_HAS_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 0 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#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 */ -#elif JS_VERSION == 13 +#elif JS_VERSION == 130 -#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_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_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 /* have break/continue to label:? */ -#define JS_HAS_DO_WHILE_LOOP 1 /* have do {...} while (b)? */ -#define JS_HAS_SWITCH_STATEMENT 1 /* have switch (v) {case c: ...}? */ -#define JS_HAS_SOME_PERL_FUN 1 /* have array.join/reverse/sort? */ -#define JS_HAS_MORE_PERL_FUN 1 /* have array.push, str.substr, etc? */ +#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 /* have apply(fun, arg1, ... argN)? */ -#define JS_HAS_OBJ_PROTO_PROP 1 /* have o.__proto__ etc.? */ -#define JS_HAS_REGEXPS 1 /* have perl r.e.s via RegExp, /pat/ */ -#define JS_HAS_SEQUENCE_OPS 1 /* have array.slice, string.concat? */ -#define JS_HAS_OBJECT_LITERAL 1 /* have var o = {'foo': 42, 'bar':3}? */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* have o.watch and o.unwatch? */ -#define JS_HAS_EXPORT_IMPORT 1 /* have export fun; import obj.fun? */ +#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 /* have === and !== identity eqops? */ -#define JS_HAS_SHARP_VARS 1 /* have #n=, #n# for object literals */ -#define JS_HAS_REPLACE_LAMBDA 1 /* have string.replace(re, lambda)? */ +#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 0 /* 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 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 */ #else diff --git a/mozilla/js/src/jsdate.c b/mozilla/js/src/jsdate.c index fe5a64c7fff..66c41bb529b 100644 --- a/mozilla/js/src/jsdate.c +++ b/mozilla/js/src/jsdate.c @@ -30,13 +30,11 @@ #include "jsconfig.h" #include "jscntxt.h" #include "jsdate.h" -#include "jslock.h" +#include "jsinterp.h" #include "jsnum.h" #include "jsobj.h" #include "jsstr.h" -#include - /* * The JS 'Date' object is patterned after the Java 'Date' object. * Here is an script: @@ -86,11 +84,12 @@ */ /* - * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 - * language definition and reduce dependence on NSPR. NSPR is used to - * get the current time in milliseconds, the time zone offset, and the - * daylight savings time offset for a given time. NSPR is also used - * for Date.toLocaleString() (only), for locale-specific formatting. + * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language + * definition and reduce dependence on NSPR. NSPR is used to get the current + * time in milliseconds, the time zone offset, and the daylight savings time + * offset for a given time. NSPR is also used for Date.toLocaleString(), for + * locale-specific formatting, and to get a string representing the timezone. + * (Which turns out to be platform-dependent.) * To do: * (I did some performance tests by timing how long it took to run what @@ -101,12 +100,12 @@ * multiple times. Although - I took a quick stab at this, and I lost * rather than gained. (Fractionally.) Hard to tell what compilers/processors * are doing these days. + * - look at tweaking function return types to return double instead * of int; this seems to make things run slightly faster sometimes. * (though it could be architecture-dependent.) It'd be good to see * how this does on win32. (Tried it on irix.) Types could use a - * general going-over. - */ + * general going-over. */ /* * Supporting functions - ECMA 15.9.1.* @@ -119,10 +118,23 @@ #define SecondsPerDay (MinutesPerDay * SecondsPerMinute) #define SecondsPerHour (MinutesPerHour * SecondsPerMinute) #define SecondsPerMinute 60.0 + +#ifdef XP_PC +/* Work around msvc double optimization bug by making these runtime values; if + * they're available at compile time, msvc optimizes division by them by + * computing the reciprocal and multiplying instead of dividing - this loses + * when the reciprocal isn't representable in a double. + */ +static jsdouble msPerSecond = 1000.0; +static jsdouble msPerDay = SecondsPerDay * 1000.0; +static jsdouble msPerHour = SecondsPerHour * 1000.0; +static jsdouble msPerMinute = SecondsPerMinute * 1000.0; +#else #define msPerDay (SecondsPerDay * msPerSecond) #define msPerHour (SecondsPerHour * msPerSecond) #define msPerMinute (SecondsPerMinute * msPerSecond) #define msPerSecond 1000.0 +#endif #define Day(t) floor((t) / msPerDay) @@ -292,13 +304,13 @@ static jsdouble LocalTZA; static jsdouble DaylightSavingTA(jsdouble t) { - int64 PR_t; - int64 ms2us; - int64 offset; + PRInt64 PR_t; + PRInt64 ms2us; + PRInt64 offset; jsdouble result; /* abort if NaN */ - if (t != t) + if (JSDOUBLE_IS_NaN(t)) return t; /* put our t in an LL, and map it to usec for prtime */ @@ -314,14 +326,10 @@ DaylightSavingTA(jsdouble t) } #define LocalTime(t) ((t) + LocalTZA + DaylightSavingTA(t)) -/* #define UTC(t) ((t) - LocalTZA - DaylightSavingTA((t) - LocalTZA)) */ + static jsdouble UTC(jsdouble t) { -/* fprintf(stderr, "time %f\n", t); */ -/* fprintf(stderr, "LocalTZA %f\n", LocalTZA); */ -/* fprintf(stderr, "DaylightSavingTA %f\n", DaylightSavingTA(t - LocalTZA)); */ - return t - LocalTZA - DaylightSavingTA(t - LocalTZA); } @@ -496,7 +504,7 @@ date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) for (loop = 0; loop < MAXARGS; loop++) { if (loop < argc) { - if (!JS_ValueToNumber(cx, argv[loop], &d)) + if (!js_ValueToNumber(cx, argv[loop], &d)) return JS_FALSE; /* return NaN if any arg is NaN */ if (!JSDOUBLE_IS_FINITE(d)) { @@ -704,7 +712,7 @@ date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSString *str; jsdouble result; - str = JS_ValueToString(cx, argv[0]); + str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; if (!date_parseString(str->chars, &result)) { @@ -717,21 +725,15 @@ date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } /* - * Check that obj is an object of class Date, - * and get the date value. Return NULL on failure, - * which should be out of band, because the value - * not publicly accessable. + * Check that obj is an object of class Date, and get the date value. + * Return NULL on failure. */ static jsdouble * -date_getProlog(JSContext *cx, JSObject *obj, jsval *argv) { - jsdouble *result; - if (!JS_InstanceOf(cx, obj, &date_class, argv)) { +date_getProlog(JSContext *cx, JSObject *obj, jsval *argv) +{ + if (!JS_InstanceOf(cx, obj, &date_class, argv)) return NULL; - } - JS_LOCK(cx); - result = JSVAL_TO_DOUBLE(OBJ_GET_SLOT(obj, JSSLOT_PRIVATE)); - JS_UNLOCK(cx); - return result; + return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE)); } /* @@ -1033,9 +1035,11 @@ date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!date) return JS_FALSE; - if (!JS_ValueToNumber(cx, argv[0], &result)) + if (!js_ValueToNumber(cx, argv[0], &result)) return JS_FALSE; + result = TIMECLIP(result); + *date = result; return js_NewNumberValue(cx, result, rval); } @@ -1062,8 +1066,21 @@ date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); + /* Satisfy the ECMA rule that if a function is called with + * fewer arguments than the specified formal arguments, the + * remaining arguments are set to undefined. Seems like all + * the Date.setWhatever functions in ECMA are only varargs + * beyond the first argument; this should be set to undefined + * if it's not given. This means that "d = new Date(); + * d.setMilliseconds()" returns NaN. Blech. + */ + if (argc == 0) + argc = 1; /* should be safe, because length of all settors is 1 */ + else if (argc > maxargs) + argc = maxargs; /* clamp argc */ + for (i = 0; i < argc; i++) { - if (!JS_ValueToNumber(cx, argv[i], &args[i])) + if (!js_ValueToNumber(cx, argv[i], &args[i])) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(args[i])) { *date = *(cx->runtime->jsNaN); @@ -1185,8 +1202,14 @@ date_makeDate(JSContext *cx, JSObject *obj, uintN argc, result = *date; + /* see complaint about ECMA in date_MakeTime */ + if (argc == 0) + argc = 1; /* should be safe, because length of all settors is 1 */ + else if (argc > maxargs) + argc = maxargs; /* clamp argc */ + for (i = 0; i < argc; i++) { - if (!JS_ValueToNumber(cx, argv[i], &args[i])) + if (!js_ValueToNumber(cx, argv[i], &args[i])) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(args[i])) { *date = *(cx->runtime->jsNaN); @@ -1293,7 +1316,7 @@ date_setYear(JSContext *cx, JSObject *obj, uintN argc, result = *date; - if (!JS_ValueToNumber(cx, argv[0], &year)) + if (!js_ValueToNumber(cx, argv[0], &year)) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(year)) { *date = *(cx->runtime->jsNaN); @@ -1402,8 +1425,17 @@ date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsdouble local = LocalTime(*date); new_explode(local, &split); - /* let PRMJTime format it. */ - PRMJ_FormatTime(buf, sizeof buf, "%c", &split); + /* let PRMJTime format it. Use '%#c' for windows, because '%c' is + * backward-compatible and non-y2k with msvc; '%#c' requests that a + * full year be used in the result string. + */ + PRMJ_FormatTime(buf, sizeof buf, +#ifdef _WIN32 + "%#c", +#else + "%c", +#endif + &split); } str = JS_NewStringCopyZ(cx, buf); @@ -1429,7 +1461,7 @@ date_format(JSContext *cx, jsdouble date, jsval *rval) /* offset from GMT in minutes. The offset includes daylight savings, if it applies. */ - jsint minutes = (jsint) floor((LocalTZA + DaylightSavingTA(local)) + jsint minutes = (jsint) floor((LocalTZA + DaylightSavingTA(date)) / msPerMinute); /* map 510 minutes to 0830 hours */ @@ -1446,7 +1478,7 @@ date_format(JSContext *cx, jsdouble date, jsval *rval) /* get a timezone string from the OS to include as a comment. */ - new_explode(local, &split); + new_explode(date, &split); PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z) ", &split); /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it @@ -1461,10 +1493,10 @@ date_format(JSContext *cx, jsdouble date, jsval *rval) SecFromTime(local), offset, - /* don't print anything for the TZA comment if we - got the empty string from the OS. */ - /* (balance: */ - (tzbuf[1] != ')' ? tzbuf : ""), + /* don't print anything for the TZA comment if we got '()' + * or something non-parenthesized from the OS. + */ + ((tzbuf[0] == '(' && tzbuf[1] != ')') ? tzbuf : ""), YearFromTime(local)); } @@ -1476,9 +1508,41 @@ date_format(JSContext *cx, jsdouble date, jsval *rval) return JS_TRUE; } +#if JS_HAS_TOSOURCE +#include "prdtoa.h" + static JSBool -date_toString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) +date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble *date; + char buf[32], *bytes; + JSString *str; + + date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + + PR_cnvtf(buf, sizeof buf, 20, *date); + bytes = PR_smprintf("(new %s(%s))", date_class.name, buf); + if (!bytes) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + str = JS_NewString(cx, bytes, strlen(bytes)); + if (!str) { + free(bytes); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +static JSBool +date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) { jsdouble *date = date_getProlog(cx, obj, argv); if (!date) @@ -1488,9 +1552,14 @@ date_toString(JSContext *cx, JSObject *obj, uintN argc, #if JS_HAS_VALUEOF_HINT static JSBool -date_valueOf(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) +date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) { + /* It is an error to call date_valueOf on a non-date object, but we don't + * need to check for that explicitly here because every path calls + * date_getProlog, which does the check. + */ + /* If called directly with no arguments, convert to a time number. */ if (argc == 0) return date_getTime(cx, obj, argc, argv, rval); @@ -1499,7 +1568,7 @@ date_valueOf(JSContext *cx, JSObject *obj, uintN argc, if (argc == 1) { JSString *str, *str2; - str = JS_ValueToString(cx, argv[0]); + str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); @@ -1545,23 +1614,26 @@ static JSFunctionSpec date_methods[] = { {"getUTCMilliseconds",date_getUTCMilliseconds,0 }, {"setTime", date_setTime, 1 }, {"setYear", date_setYear, 1 }, - {"setFullYear", date_setFullYear, 1 }, - {"setUTCFullYear", date_setUTCFullYear, 1 }, - {"setMonth", date_setMonth, 1 }, - {"setUTCMonth", date_setUTCMonth, 1 }, + {"setFullYear", date_setFullYear, 3 }, + {"setUTCFullYear", date_setUTCFullYear, 3 }, + {"setMonth", date_setMonth, 2 }, + {"setUTCMonth", date_setUTCMonth, 2 }, {"setDate", date_setDate, 1 }, {"setUTCDate", date_setUTCDate, 1 }, - {"setHours", date_setHours, 1 }, - {"setUTCHours", date_setUTCHours, 1 }, - {"setMinutes", date_setMinutes, 1 }, - {"setUTCMinutes", date_setUTCMinutes, 1 }, - {"setSeconds", date_setSeconds, 1 }, - {"setUTCSeconds", date_setUTCSeconds, 1 }, + {"setHours", date_setHours, 4 }, + {"setUTCHours", date_setUTCHours, 4 }, + {"setMinutes", date_setMinutes, 3 }, + {"setUTCMinutes", date_setUTCMinutes, 3 }, + {"setSeconds", date_setSeconds, 2 }, + {"setUTCSeconds", date_setUTCSeconds, 2 }, {"setMilliseconds", date_setMilliseconds, 1 }, {"setUTCMilliseconds",date_setUTCMilliseconds,1 }, {"toGMTString", date_toGMTString, 0 }, {"toUTCString", date_toGMTString, 0 }, {"toLocaleString", date_toLocaleString, 0 }, +#if JS_HAS_TOSOURCE + {js_toSource_str, date_toSource, 0 }, +#endif {js_toString_str, date_toString, 0 }, {js_valueOf_str, date_valueOf, 0 }, {0} @@ -1575,11 +1647,7 @@ date_constructor(JSContext *cx, JSObject* obj) date = js_NewDouble(cx, 0.0); if (!date) return NULL; - - JS_LOCK(cx); - OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date)); - JS_UNLOCK(cx); - + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date)); return date; } @@ -1591,8 +1659,8 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jsdouble d; /* Date called as function */ - if (obj->map->clasp != &date_class) { - int64 us, ms, us2ms; + if (!cx->fp->constructing) { + PRInt64 us, ms, us2ms; jsdouble time; /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS', @@ -1608,7 +1676,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) /* Date called as constructor */ if (argc == 0) { - int64 us, ms, us2ms; + PRInt64 us, ms, us2ms; jsdouble time; date = date_constructor(cx, obj); @@ -1624,7 +1692,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } else if (argc == 1) { if (!JSVAL_IS_STRING(argv[0])) { /* the argument is a millisecond number */ - if (!JS_ValueToNumber(cx, argv[0], &d)) + if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; date = date_constructor(cx, obj); if (!date) @@ -1636,7 +1704,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!date) return JS_FALSE; - str = JS_ValueToString(cx, argv[0]); + str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; @@ -1653,7 +1721,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) for (loop = 0; loop < MAXARGS; loop++) { if (loop < argc) { - if (!JS_ValueToNumber(cx, argv[loop], &d)) + if (!js_ValueToNumber(cx, argv[loop], &d)) return JS_FALSE; /* if any arg is NaN, make a NaN date object and return */ @@ -1684,24 +1752,10 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) array[2] = 1; day = MakeDay(array[0], array[1], array[2]); - -/* fprintf(stderr, "%f\n", day); */ - time = MakeTime(array[3], array[4], array[5], array[6]); - -/* fprintf(stderr, "%f\n", time); */ - time = MakeDate(day, time); - -/* fprintf(stderr, "%f\n", time); */ - time = UTC(time); - -/* fprintf(stderr, "%f\n", time); */ - *date = TIMECLIP(time); - -/* fprintf(stderr, "%f\n", *date); */ } return JS_TRUE; } @@ -1709,10 +1763,23 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSObject * js_InitDateClass(JSContext *cx, JSObject *obj) { + JSObject *proto; + jsdouble *proto_date; + /* set static LocalTZA */ LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); - return JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS, - NULL, date_methods, NULL, date_static_methods); + proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS, + NULL, date_methods, NULL, date_static_methods); + if (!proto) + return NULL; + + /* Set the value of the Date.prototype date to NaN */ + proto_date = date_constructor(cx, proto); + if (!proto_date) + return NULL; + *proto_date = *(cx->runtime->jsNaN); + + return proto; } JS_FRIEND_API(JSObject *) @@ -1807,7 +1874,7 @@ js_DateSetYear(JSContext *cx, JSObject *obj, int year) return; local = LocalTime(*date); /* reset date if it was NaN */ - if (local != local) + if (JSDOUBLE_IS_NaN(local)) local = 0; local = date_msecFromDate(year, MonthFromTime(local), @@ -1828,7 +1895,7 @@ js_DateSetMonth(JSContext *cx, JSObject *obj, int month) return; local = LocalTime(*date); /* bail if date was NaN */ - if (local != local) + if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), month, @@ -1848,7 +1915,7 @@ js_DateSetDate(JSContext *cx, JSObject *obj, int date) if (!datep) return; local = LocalTime(*datep); - if (local != local) + if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), MonthFromTime(local), @@ -1868,7 +1935,7 @@ js_DateSetHours(JSContext *cx, JSObject *obj, int hours) if (!date) return; local = LocalTime(*date); - if (local != local) + if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), MonthFromTime(local), @@ -1888,7 +1955,7 @@ js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes) if (!date) return; local = LocalTime(*date); - if (local != local) + if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), MonthFromTime(local), @@ -1908,7 +1975,7 @@ js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds) if (!date) return; local = LocalTime(*date); - if (local != local) + if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), MonthFromTime(local), diff --git a/mozilla/js/src/jsdate.h b/mozilla/js/src/jsdate.h index 68248a504a5..8d2d29a5c4d 100644 --- a/mozilla/js/src/jsdate.h +++ b/mozilla/js/src/jsdate.h @@ -52,22 +52,22 @@ js_DateGetMinutes(JSContext *cx, JSObject* obj); extern JS_FRIEND_API(int) js_DateGetSeconds(JSContext *cx, JSObject* obj); -extern JS_FRIEND_API(void) +extern JS_FRIEND_API(void) js_DateSetYear(JSContext *cx, JSObject *obj, int year); -extern JS_FRIEND_API(void) +extern JS_FRIEND_API(void) js_DateSetMonth(JSContext *cx, JSObject *obj, int year); -extern JS_FRIEND_API(void) +extern JS_FRIEND_API(void) js_DateSetDate(JSContext *cx, JSObject *obj, int date); -extern JS_FRIEND_API(void) +extern JS_FRIEND_API(void) js_DateSetHours(JSContext *cx, JSObject *obj, int hours); -extern JS_FRIEND_API(void) +extern JS_FRIEND_API(void) js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes); -extern JS_FRIEND_API(void) +extern JS_FRIEND_API(void) js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds); diff --git a/mozilla/js/src/jsdbgapi.c b/mozilla/js/src/jsdbgapi.c index bf732228edb..57d1e69aa59 100644 --- a/mozilla/js/src/jsdbgapi.c +++ b/mozilla/js/src/jsdbgapi.c @@ -24,16 +24,18 @@ #include "prlog.h" #include "prclist.h" #include "jsapi.h" +#include "jscntxt.h" +#include "jsconfig.h" #include "jsdbgapi.h" +#include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" +#include "jslock.h" #include "jsobj.h" #include "jsopcode.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" -#include "jscntxt.h" -#include "jsfun.h" typedef struct JSTrap { PRCList links; @@ -70,7 +72,7 @@ js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op) *pc = (jsbytecode)op; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, void *closure) { @@ -84,7 +86,7 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, *pc = (jsbytecode)trap->op; } else { trap = JS_malloc(cx, sizeof *trap); - if (!trap || !js_AddRoot(cx, &trap->closure)) { + if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) { if (trap) JS_free(cx, trap); return JS_FALSE; @@ -100,7 +102,7 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, return JS_TRUE; } -PR_IMPLEMENT(JSOp) +JS_PUBLIC_API(JSOp) JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) { JSTrap *trap; @@ -122,7 +124,7 @@ DestroyTrap(JSContext *cx, JSTrap *trap) JS_free(cx, trap); } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler *handlerp, void **closurep) { @@ -137,7 +139,7 @@ JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, DestroyTrap(cx, trap); } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ClearScriptTraps(JSContext *cx, JSScript *script) { JSRuntime *rt; @@ -153,7 +155,7 @@ JS_ClearScriptTraps(JSContext *cx, JSScript *script) } } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ClearAllTraps(JSContext *cx) { JSRuntime *rt; @@ -168,26 +170,32 @@ JS_ClearAllTraps(JSContext *cx) } } -PR_IMPLEMENT(JSTrapStatus) +JS_PUBLIC_API(JSTrapStatus) JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval) { JSTrap *trap; JSTrapStatus status; + jsint op; trap = FindTrap(cx->runtime, script, pc); if (!trap) { PR_ASSERT(0); /* XXX can't happen */ return JSTRAP_ERROR; } + /* + * It's important that we not use 'trap->' after calling the callback -- + * the callback might remove the trap! + */ + op = (jsint)trap->op; status = trap->handler(cx, script, pc, rval, trap->closure); if (status == JSTRAP_CONTINUE) { /* By convention, return the true op to the interpreter in rval. */ - *rval = INT_TO_JSVAL((jsint)trap->op); + *rval = INT_TO_JSVAL(op); } return status; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure) { rt->interruptHandler = handler; @@ -195,11 +203,11 @@ JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure) return JS_TRUE; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep) { if (handlerp) - *handlerp = rt->interruptHandler; + *handlerp = (JSTrapHandler)rt->interruptHandler; if (closurep) *closurep = rt->interruptHandlerData; rt->interruptHandler = 0; @@ -212,7 +220,7 @@ typedef struct JSWatchPoint { PRCList links; JSObject *object; /* weak link, see js_FinalizeObject */ jsval userid; - JSProperty *prop; + JSScopeProperty *sprop; JSPropertyOp setter; JSWatchPointHandler handler; void *closure; @@ -226,8 +234,10 @@ DropWatchPoint(JSContext *cx, JSWatchPoint *wp) { if (--wp->nrefs != 0) return; - wp->prop->setter = wp->setter; - js_DropProperty(cx, wp->prop); + wp->sprop->setter = wp->setter; + JS_LOCK_OBJ_VOID(cx, wp->object, + js_DropScopeProperty(cx, (JSScope *)wp->object->map, + wp->sprop)); PR_REMOVE_LINK(&wp->links); js_RemoveRoot(cx, &wp->closure); JS_free(cx, wp); @@ -247,7 +257,7 @@ FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid) return NULL; } -JSProperty * +JSScopeProperty * js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid) { JSWatchPoint *wp; @@ -255,7 +265,7 @@ js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid) wp = FindWatchPoint(rt, obj, userid); if (!wp) return NULL; - return wp->prop; + return wp->sprop; } JSBool PR_CALLBACK @@ -263,9 +273,10 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSRuntime *rt; JSWatchPoint *wp; - JSProperty *prop; + JSScopeProperty *sprop; JSSymbol *sym; - jsval symid, value; + jsval userid, value; + jsid symid; JSScope *scope; JSAtom *atom; JSBool ok; @@ -274,30 +285,43 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = (JSWatchPoint *)wp->links.next) { - prop = wp->prop; - if (wp->object == obj && prop->id == id) { - sym = prop->symbols; + sprop = wp->sprop; + if (wp->object == obj && sprop->id == id) { + JS_LOCK_OBJ(cx, obj); + sym = sprop->symbols; if (!sym) { - symid = wp->userid; + userid = wp->userid; atom = NULL; - if (JSVAL_IS_STRING(symid)) { - atom = js_ValueToStringAtom(cx, symid); - if (!atom) + if (JSVAL_IS_INT(userid)) { + symid = (jsid)userid; + } else { + atom = js_ValueToStringAtom(cx, userid); + if (!atom) { + JS_UNLOCK_OBJ(cx, obj); return JS_FALSE; - symid = (jsval)atom; + } + symid = (jsid)atom; } scope = (JSScope *) obj->map; - ok = obj->map->clasp->addProperty(cx, obj, prop->id, &value) && - scope->ops->add(cx, scope, symid, prop); - if (atom) - js_DropAtom(cx, atom); - if (!ok) + PR_ASSERT(scope->props); + ok = LOCKED_OBJ_GET_CLASS(obj)->addProperty(cx, obj, sprop->id, + &value); + if (!ok) { + JS_UNLOCK_OBJ(cx, obj); return JS_FALSE; - sym = prop->symbols; + } + ok = (scope->ops->add(cx, scope, symid, sprop) != NULL); + if (!ok) { + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; + } + sym = sprop->symbols; } + JS_UNLOCK_OBJ(cx, obj); HoldWatchPoint(wp); ok = wp->handler(cx, obj, js_IdToValue(sym_id(sym)), - obj->slots[wp->prop->slot], vp, wp->closure); + OBJ_GET_SLOT(cx, obj, wp->sprop->slot), vp, + wp->closure); if (ok) ok = wp->setter(cx, obj, id, vp); DropWatchPoint(cx, wp); @@ -308,45 +332,77 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_FALSE; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler handler, void *closure) { JSAtom *atom; - jsval symid; - JSProperty *prop; + jsid symid; + JSObject *pobj; + JSScopeProperty *sprop; JSRuntime *rt; JSWatchPoint *wp; + if (!OBJ_IS_NATIVE(obj)) { + JS_ReportError(cx, "can't watch non-native objects of class %s", + OBJ_GET_CLASS(cx, obj)->name); + return JS_FALSE; + } + if (JSVAL_IS_INT(id)) { - symid = id; + symid = (jsid)id; atom = NULL; } else { atom = js_ValueToStringAtom(cx, id); if (!atom) return JS_FALSE; - symid = (jsval)atom; + symid = (jsid)atom; } - if (!js_LookupProperty(cx, obj, symid, &obj, &prop)) + if (!js_LookupProperty(cx, obj, symid, &pobj, (JSProperty **)&sprop)) return JS_FALSE; rt = cx->runtime; - if (!prop) { + if (!sprop) { /* Check for a deleted symbol watchpoint, which holds its property. */ - prop = js_FindWatchPoint(rt, obj, id); - if (!prop) { + sprop = js_FindWatchPoint(rt, obj, id); + if (sprop) { +#ifdef JS_THREADSAFE + /* Emulate js_LookupProperty if thread-safe. */ + JS_LOCK_OBJ(cx, obj); + sprop->nrefs++; +#endif + } else { /* Make a new property in obj so we can watch for the first set. */ - prop = js_DefineProperty(cx, obj, symid, JSVAL_VOID, NULL, NULL, 0); + if (!js_DefineProperty(cx, obj, symid, JSVAL_VOID, NULL, NULL, 0, + (JSProperty **)&sprop)) { + sprop = NULL; + } } - } else if (prop->object != obj) { + } else if (pobj != obj) { /* Clone the prototype property so we can watch the right object. */ - prop = js_DefineProperty(cx, obj, symid, - prop->object->slots[prop->slot], - prop->getter, prop->setter, prop->flags); + jsval value; + JSPropertyOp getter, setter; + uintN attrs; + + if (OBJ_IS_NATIVE(pobj)) { + value = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); + } else { + if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) { + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + return JS_FALSE; + } + } + getter = sprop->getter; + setter = sprop->setter; + attrs = sprop->attrs; + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + + if (!js_DefineProperty(cx, obj, symid, value, getter, setter, attrs, + (JSProperty **)&sprop)) { + sprop = NULL; + } } - if (atom) - js_DropAtom(cx, atom); - if (!prop) + if (!sprop) return JS_FALSE; wp = FindWatchPoint(rt, obj, id); @@ -354,24 +410,25 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, wp = JS_malloc(cx, sizeof *wp); if (!wp) return JS_FALSE; - if (!js_AddRoot(cx, &wp->closure)) { + if (!js_AddRoot(cx, &wp->closure, "wp->closure")) { JS_free(cx, wp); return JS_FALSE; } PR_APPEND_LINK(&wp->links, &rt->watchPointList); wp->object = obj; wp->userid = id; - wp->prop = js_HoldProperty(cx, prop); - wp->setter = prop->setter; - prop->setter = js_watch_set; + wp->sprop = js_HoldScopeProperty(cx, (JSScope *)obj->map, sprop); + wp->setter = sprop->setter; + sprop->setter = js_watch_set; wp->nrefs = 1; } wp->handler = handler; wp->closure = closure; + OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop); return JS_TRUE; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler *handlerp, void **closurep) { @@ -397,7 +454,7 @@ JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, *closurep = NULL; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj) { JSRuntime *rt; @@ -413,7 +470,7 @@ JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj) } } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_ClearAllWatchPoints(JSContext *cx) { JSRuntime *rt; @@ -428,25 +485,25 @@ JS_ClearAllWatchPoints(JSContext *cx) } } -PR_IMPLEMENT(uintN) +JS_PUBLIC_API(uintN) JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) { return js_PCToLineNumber(script, pc); } -PR_IMPLEMENT(jsbytecode *) +JS_PUBLIC_API(jsbytecode *) JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno) { return js_LineNumberToPC(script, lineno); } -PR_IMPLEMENT(JSScript *) +JS_PUBLIC_API(JSScript *) JS_GetFunctionScript(JSContext *cx, JSFunction *fun) { return fun->script; } -PR_IMPLEMENT(JSPrincipals *) +JS_PUBLIC_API(JSPrincipals *) JS_GetScriptPrincipals(JSContext *cx, JSScript *script) { return script->principals; @@ -456,26 +513,26 @@ JS_GetScriptPrincipals(JSContext *cx, JSScript *script) /* * Stack Frame Iterator */ -PR_IMPLEMENT(JSStackFrame *) +JS_PUBLIC_API(JSStackFrame *) JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp) { *iteratorp = (*iteratorp == NULL) ? cx->fp : (*iteratorp)->down; return *iteratorp; } -PR_IMPLEMENT(JSScript *) +JS_PUBLIC_API(JSScript *) JS_GetFrameScript(JSContext *cx, JSStackFrame *fp) { return fp->script; } -PR_IMPLEMENT(jsbytecode *) +JS_PUBLIC_API(jsbytecode *) JS_GetFramePC(JSContext *cx, JSStackFrame *fp) { return fp->pc; } -PR_IMPLEMENT(void *) +JS_PUBLIC_API(void *) JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp) { if (fp->annotation) { @@ -498,13 +555,13 @@ JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp) return NULL; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation) { fp->annotation = annotation; } -PR_IMPLEMENT(void *) +JS_PUBLIC_API(void *) JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp) { JSPrincipals *principals = fp->script @@ -516,25 +573,25 @@ JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp) : NULL; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp) { return fp->fun && fp->fun->call; } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_GetFrameObject(JSContext *cx, JSStackFrame *fp) { return fp->scopeChain; } -PR_IMPLEMENT(JSObject *) +JS_PUBLIC_API(JSObject *) JS_GetFrameThis(JSContext *cx, JSStackFrame *fp) { return fp->thisp; } -PR_IMPLEMENT(JSFunction *) +JS_PUBLIC_API(JSFunction *) JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp) { return fp->fun; @@ -542,19 +599,19 @@ JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp) /************************************************************************/ -PR_IMPLEMENT(const char *) +JS_PUBLIC_API(const char *) JS_GetScriptFilename(JSContext *cx, JSScript *script) { return script->filename; } -PR_IMPLEMENT(uintN) +JS_PUBLIC_API(uintN) JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script) { return script->lineno; } -PR_IMPLEMENT(uintN) +JS_PUBLIC_API(uintN) JS_GetScriptLineExtent(JSContext *cx, JSScript *script) { return js_GetScriptLineExtent(script); @@ -562,25 +619,24 @@ JS_GetScriptLineExtent(JSContext *cx, JSScript *script) /***************************************************************************/ -PR_IMPLEMENT(void) -JS_SetNewScriptHookProc(JSRuntime *rt, JSNewScriptHookProc hookproc, - void *callerdata) +JS_PUBLIC_API(void) +JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata) { - rt->newScriptHookProcData = callerdata; - rt->newScriptHookProc = hookproc; + rt->newScriptHook = hook; + rt->newScriptHookData = callerdata; } -PR_IMPLEMENT(void) -JS_SetDestroyScriptHookProc(JSRuntime *rt, JSDestroyScriptHookProc hookproc, - void *callerdata) +JS_PUBLIC_API(void) +JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, + void *callerdata) { - rt->destroyScriptHookProcData = callerdata; - rt->destroyScriptHookProc = hookproc; + rt->destroyScriptHook = hook; + rt->destroyScriptHookData = callerdata; } /***************************************************************************/ -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, const char *bytes, uintN length, const char *filename, uintN lineno, @@ -595,42 +651,45 @@ JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, bytes, length, filename, lineno); if (!script) return JS_FALSE; - ok = js_Execute(cx, fp->scopeChain, script, fp, rval); - JS_DestroyScript(cx, script); + ok = js_Execute(cx, fp->scopeChain, script, fp->fun, fp, JS_TRUE, rval); + js_DestroyScript(cx, script); return ok; } /************************************************************************/ -PR_IMPLEMENT(JSProperty *) -JS_PropertyIterator(JSObject *obj, JSProperty **iteratorp) +JS_PUBLIC_API(JSScopeProperty *) +JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp) { - JSProperty *prop; + JSScopeProperty *sprop; + JSScope *scope; - prop = *iteratorp; - prop = (prop == NULL) ? obj->map->props : prop->next; - *iteratorp = prop; - return prop; + sprop = *iteratorp; + scope = (JSScope *) obj->map; + sprop = (sprop == NULL) ? scope->props : sprop->next; + *iteratorp = sprop; + return sprop; } -PR_IMPLEMENT(JSBool) -JS_GetPropertyDesc(JSContext *cx, JSProperty *prop, JSPropertyDesc *pd) +JS_PUBLIC_API(JSBool) +JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, + JSPropertyDesc *pd) { JSSymbol *sym; - sym = prop->symbols; + sym = sprop->symbols; pd->id = sym ? js_IdToValue(sym_id(sym)) : JSVAL_VOID; - pd->value = prop->object->slots[prop->slot]; - pd->flags = ((prop->flags & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) - | ((prop->flags & JSPROP_READONLY) ? JSPD_READONLY : 0) - | ((prop->flags & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) - | ((prop->getter == js_GetArgument) ? JSPD_ARGUMENT : 0) - | ((prop->getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); + pd->value = OBJ_GET_SLOT(cx, obj, sprop->slot); + pd->flags = ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) + | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) + | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) + | ((sprop->getter == js_GetArgument) ? JSPD_ARGUMENT : 0) + | ((sprop->getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); pd->spare = 0; - pd->slot = (prop->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)) - ? JSVAL_TO_INT(prop->id) + pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)) + ? JSVAL_TO_INT(sprop->id) : 0; - if (!sym || !sym->next || (prop->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))) { + if (!sym || !sym->next || (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))) { pd->alias = JSVAL_VOID; } else { pd->alias = js_IdToValue(sym_id(sym->next)); @@ -639,18 +698,18 @@ JS_GetPropertyDesc(JSContext *cx, JSProperty *prop, JSPropertyDesc *pd) return JS_TRUE; } -PR_IMPLEMENT(JSBool) +JS_PUBLIC_API(JSBool) JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda) { JSScope *scope; uint32 i, n; JSPropertyDesc *pd; - JSProperty *prop; + JSScopeProperty *sprop; - if (!obj->map->clasp->enumerate(cx, obj)) + if (!OBJ_GET_CLASS(cx, obj)->enumerate(cx, obj)) return JS_FALSE; scope = (JSScope *)obj->map; - if (scope->object != obj) { + if (!scope->props) { pda->length = 0; pda->array = NULL; return JS_TRUE; @@ -660,11 +719,13 @@ JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda) if (!pd) return JS_FALSE; i = 0; - for (prop = scope->map.props; prop; prop = prop->next) { - if (!js_AddRoot(cx, &pd[i].id) || !js_AddRoot(cx, &pd[i].value)) + for (sprop = scope->props; sprop; sprop = sprop->next) { + if (!js_AddRoot(cx, &pd[i].id, NULL)) goto bad; - JS_GetPropertyDesc(cx, prop, &pd[i]); - if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias)) + if (!js_AddRoot(cx, &pd[i].value, NULL)) + goto bad; + JS_GetPropertyDesc(cx, obj, sprop, &pd[i]); + if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL)) goto bad; if (++i == n) break; @@ -680,7 +741,7 @@ bad: return JS_FALSE; } -PR_IMPLEMENT(void) +JS_PUBLIC_API(void) JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda) { JSPropertyDesc *pd; diff --git a/mozilla/js/src/jsdbgapi.h b/mozilla/js/src/jsdbgapi.h index 61464ce48e9..66b2f09d6b0 100644 --- a/mozilla/js/src/jsdbgapi.h +++ b/mozilla/js/src/jsdbgapi.h @@ -27,44 +27,33 @@ PR_BEGIN_EXTERN_C -typedef enum JSTrapStatus { - JSTRAP_ERROR, - JSTRAP_CONTINUE, - JSTRAP_RETURN, - JSTRAP_LIMIT -} JSTrapStatus; - -typedef JSTrapStatus -(*JSTrapHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, - void *closure); - extern void js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, void *closure); -PR_EXTERN(JSOp) +extern JS_PUBLIC_API(JSOp) JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler *handlerp, void **closurep); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ClearScriptTraps(JSContext *cx, JSScript *script); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ClearAllTraps(JSContext *cx); -PR_EXTERN(JSTrapStatus) +extern JS_PUBLIC_API(JSTrapStatus) JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep); /************************************************************************/ @@ -73,32 +62,44 @@ typedef JSBool (*JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *newp, void *closure); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler handler, void *closure); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler *handlerp, void **closurep); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_ClearAllWatchPoints(JSContext *cx); +#ifdef JS_HAS_OBJ_WATCHPOINT +/* + * Hide these non-API function prototypes by testing whether the internal + * header file "jsconfig.h" has been included. + */ +extern JSScopeProperty * +js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid); + +extern JSBool PR_CALLBACK +js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp); +#endif + /************************************************************************/ -PR_EXTERN(uintN) +extern JS_PUBLIC_API(uintN) JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); -PR_EXTERN(jsbytecode *) +extern JS_PUBLIC_API(jsbytecode *) JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); -PR_EXTERN(JSScript *) +extern JS_PUBLIC_API(JSScript *) JS_GetFunctionScript(JSContext *cx, JSFunction *fun); -PR_EXTERN(JSPrincipals *) +extern JS_PUBLIC_API(JSPrincipals *) JS_GetScriptPrincipals(JSContext *cx, JSScript *script); /* @@ -108,77 +109,67 @@ JS_GetScriptPrincipals(JSContext *cx, JSScript *script); * information from the frames. */ -PR_EXTERN(JSStackFrame *) +extern JS_PUBLIC_API(JSStackFrame *) JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp); -PR_EXTERN(JSScript *) +extern JS_PUBLIC_API(JSScript *) JS_GetFrameScript(JSContext *cx, JSStackFrame *fp); -PR_EXTERN(jsbytecode *) +extern JS_PUBLIC_API(jsbytecode *) JS_GetFramePC(JSContext *cx, JSStackFrame *fp); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp); -PR_EXTERN(void *) +extern JS_PUBLIC_API(void *) JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation); -PR_EXTERN(void *) +extern JS_PUBLIC_API(void *) JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_GetFrameObject(JSContext *cx, JSStackFrame *fp); -PR_EXTERN(JSObject *) +extern JS_PUBLIC_API(JSObject *) JS_GetFrameThis(JSContext *cx, JSStackFrame *fp); -PR_EXTERN(JSFunction *) +extern JS_PUBLIC_API(JSFunction *) JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp); /************************************************************************/ -PR_EXTERN(const char *) +extern JS_PUBLIC_API(const char *) JS_GetScriptFilename(JSContext *cx, JSScript *script); -PR_EXTERN(uintN) +extern JS_PUBLIC_API(uintN) JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script); -PR_EXTERN(uintN) +extern JS_PUBLIC_API(uintN) JS_GetScriptLineExtent(JSContext *cx, JSScript *script); /************************************************************************/ -/* called just after script creation */ -typedef void -(*JSNewScriptHookProc)( - JSContext *cx, - const char *filename, /* URL this script loads from */ - uintN lineno, /* line where this script starts */ - JSScript *script, - JSFunction *fun, - void *callerdata ); +/* + * Hook setters for script creation and destruction, see jsprvtd.h for the + * typedefs. These macros provide binary compatibility and newer, shorter + * synonyms. + */ +#define JS_SetNewScriptHook JS_SetNewScriptHookProc +#define JS_SetDestroyScriptHook JS_SetDestroyScriptHookProc -/* called just before script destruction */ -typedef void -(*JSDestroyScriptHookProc)( - JSContext *cx, - JSScript *script, - void *callerdata ); +extern JS_PUBLIC_API(void) +JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata); -PR_EXTERN(void) -JS_SetNewScriptHookProc(JSRuntime *rt, JSNewScriptHookProc hookproc, +extern JS_PUBLIC_API(void) +JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, void *callerdata); -PR_EXTERN(void) -JS_SetDestroyScriptHookProc(JSRuntime *rt, JSDestroyScriptHookProc hookproc, - void *callerdata); - /************************************************************************/ -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, const char *bytes, uintN length, const char *filename, uintN lineno, @@ -207,16 +198,17 @@ typedef struct JSPropertyDescArray { JSPropertyDesc *array; /* alloc'd by Get, freed by Put */ } JSPropertyDescArray; -PR_EXTERN(JSProperty *) -JS_PropertyIterator(JSObject *obj, JSProperty **iteratorp); +extern JS_PUBLIC_API(JSScopeProperty *) +JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp); -PR_EXTERN(JSBool) -JS_GetPropertyDesc(JSContext *cx, JSProperty *prop, JSPropertyDesc *pd); +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, + JSPropertyDesc *pd); -PR_EXTERN(JSBool) +extern JS_PUBLIC_API(JSBool) JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda); -PR_EXTERN(void) +extern JS_PUBLIC_API(void) JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda); PR_END_EXTERN_C diff --git a/mozilla/js/src/jsemit.c b/mozilla/js/src/jsemit.c index a1b0c0f4cb0..1cfa89603b2 100644 --- a/mozilla/js/src/jsemit.c +++ b/mozilla/js/src/jsemit.c @@ -31,28 +31,48 @@ #include "prlog.h" #include "jsapi.h" #include "jscntxt.h" +#include "jsconfig.h" #include "jsemit.h" +#include "jsfun.h" +#include "jsnum.h" #include "jsopcode.h" +#include "jsparse.h" +#include "jsscript.h" #define CGINCR 256 /* code generation bytecode allocation increment */ #define SNINCR 64 /* source note allocation increment */ JS_FRIEND_API(JSBool) -js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, PRArenaPool *pool) +js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, + const char *filename, uintN lineno, + JSPrincipals *principals) { - cg->pool = pool; - PR_ARENA_ALLOCATE(cg->base, pool, CGINCR); + cg->codeMark = PR_ARENA_MARK(&cx->codePool); + cg->tempMark = PR_ARENA_MARK(&cx->tempPool); + PR_ARENA_ALLOCATE(cg->base, &cx->codePool, CGINCR); if (!cg->base) { JS_ReportOutOfMemory(cx); return JS_FALSE; } cg->limit = CG_CODE(cg, CGINCR); + cg->filename = filename; + cg->firstLine = lineno; + cg->principals = principals; + cg->tryBase = cg->tryLimit = NULL; CG_RESET(cg); return JS_TRUE; } +JS_FRIEND_API(void) +js_ResetCodeGenerator(JSContext *cx, JSCodeGenerator *cg) +{ + PR_ARENA_RELEASE(&cx->codePool, cg->codeMark); + PR_ARENA_RELEASE(&cx->tempPool, cg->tempMark); + CG_RESET(cg); +} + static ptrdiff_t -EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, intN delta) +EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta) { ptrdiff_t offset, length; size_t cgsize; @@ -61,116 +81,38 @@ EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, intN delta) offset = CG_OFFSET(cg); if (op != JSOP_NOP) cg->lastCodeOffset = offset; - if ((pruword)cg->ptr + delta >= (pruword)cg->limit) { + if ((pruword)cg->next + delta >= (pruword)cg->limit) { length = cg->limit - cg->base; cgsize = length * sizeof(jsbytecode); - PR_ARENA_GROW(cg->base, cg->pool, cgsize, CGINCR); + PR_ARENA_GROW(cg->base, &cx->codePool, cgsize, CGINCR); if (!cg->base) { JS_ReportOutOfMemory(cx); return -1; } cg->limit = CG_CODE(cg, length + CGINCR); - cg->ptr = CG_CODE(cg, offset); + cg->next = CG_CODE(cg, offset); } return offset; } -/* - * Cancel the last instruction. This can't be called more than once between - * bytecode generations using cg. Most of the work involves finding and then - * cancelling any related source notes. - */ -void -js_CancelLastOpcode(JSContext *cx, JSCodeGenerator *cg, uint16 *newlinesp) -{ - ptrdiff_t offset, lastCodeOffset; - ptrdiff_t shrink, delta; - JSCodeSpec *cs; - jssrcnote *sn, *last, *next; - - lastCodeOffset = cg->lastCodeOffset; - shrink = CG_OFFSET(cg) - lastCodeOffset; - if (shrink <= 0) - return; - - cg->ptr = CG_CODE(cg, lastCodeOffset); - cs = &js_CodeSpec[*cg->ptr]; - PR_ASSERT(cs->nuses >= 0); - cg->stackDepth += cs->nuses; - cg->stackDepth -= cs->ndefs; - - if (cg->noteCount) { - offset = cg->saveNoteOffset; - last = &cg->notes[cg->lastNoteCount]; - for (sn = &cg->notes[cg->saveNoteCount]; sn <= last; sn = next) { - next = SN_NEXT(sn); - delta = SN_DELTA(sn); - offset += delta; - if (offset == lastCodeOffset) { - /* The canceled op may not have any non-newline note. */ - if (SN_IS_GETTABLE(sn)) { - SN_MAKE_TERMINATOR(sn); - cg->noteCount = cg->saveNoteCount; - cg->lastNoteOffset = cg->saveNoteOffset; - - /* Clear so we'll know to increment *newlinesp below. */ - shrink = 0; - } - } else if (offset > lastCodeOffset) { - /* Notes after the canceled op are newlines or xdeltas. */ - if (shrink == 0) { - if (SN_TYPE(sn) == SRC_NEWLINE) { - /* Newlines after the canceled op are pushed back. */ - (*newlinesp)++; - } - } else if (delta >= shrink) { - /* If there's enough delta to take away shrink, do it. */ - delta -= shrink; - SN_SET_DELTA(sn, delta); - } - } - } - } -} - -void -js_UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) +static void +UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) { jsbytecode *pc; - jssrcnote *sn, *last; - ptrdiff_t offset; JSCodeSpec *cs; intN nuses; pc = CG_CODE(cg, target); - - /* - * If there are source notes, check for hidden JSOP_LEAVEWITH created when - * breaking or continuing out of a with statement, or a JSOP_POP generated - * when breaking or continuing out of a for/in loop. The compiler ensures - * stack balance for such bytecodes, but if we account for their popping - * here, we'll misreport an underflow. - */ - if (cg->noteCount) { - sn = cg->notes; - last = &sn[cg->saveNoteCount]; - if (SN_TYPE(last) == SRC_HIDDEN) { - /* We must ensure that this source note applies to target. */ - for (offset = 0; sn <= last; sn = SN_NEXT(sn)) { - offset += SN_DELTA(sn); - if (offset == target && sn == last) - return; - } - } - } - cs = &js_CodeSpec[pc[0]]; nuses = cs->nuses; if (nuses < 0) nuses = 2 + GET_ARGC(pc); /* stack: fun, this, [argc arguments] */ cg->stackDepth -= nuses; - if (cg->stackDepth < 0) - JS_ReportError(cx, "internal error: stack underflow at pc %d", target); + if (cg->stackDepth < 0) { + JS_ReportError(cx, + "internal error compiling %s: stack underflow at pc %d", + cg->filename ? cg->filename : "stdin", target); + } cg->stackDepth += cs->ndefs; if ((uintN)cg->stackDepth > cg->maxStackDepth) cg->maxStackDepth = cg->stackDepth; @@ -182,8 +124,8 @@ js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op) ptrdiff_t offset = EmitCheck(cx, cg, op, 1); if (offset >= 0) { - *cg->ptr++ = (jsbytecode)op; - js_UpdateDepth(cx, cg, offset); + *cg->next++ = (jsbytecode)op; + UpdateDepth(cx, cg, offset); } return offset; } @@ -194,10 +136,10 @@ js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1) ptrdiff_t offset = EmitCheck(cx, cg, op, 2); if (offset >= 0) { - cg->ptr[0] = (jsbytecode)op; - cg->ptr[1] = op1; - cg->ptr += 2; - js_UpdateDepth(cx, cg, offset); + cg->next[0] = (jsbytecode)op; + cg->next[1] = op1; + cg->next += 2; + UpdateDepth(cx, cg, offset); } return offset; } @@ -209,34 +151,51 @@ js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, ptrdiff_t offset = EmitCheck(cx, cg, op, 3); if (offset >= 0) { - cg->ptr[0] = (jsbytecode)op; - cg->ptr[1] = op1; - cg->ptr[2] = op2; - cg->ptr += 3; - js_UpdateDepth(cx, cg, offset); + cg->next[0] = (jsbytecode)op; + cg->next[1] = op1; + cg->next[2] = op2; + cg->next += 3; + UpdateDepth(cx, cg, offset); + } + return offset; +} + +ptrdiff_t +js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra) +{ + ptrdiff_t length = 1 + (ptrdiff_t)extra; + ptrdiff_t offset = EmitCheck(cx, cg, op, length); + + if (offset >= 0) { + *cg->next = (jsbytecode)op; + cg->next += length; + UpdateDepth(cx, cg, offset); } return offset; } static const char *statementName[] = { - "block", /* BLOCK */ - "label statement", /* LABEL */ - "if statement", /* IF */ - "else statement", /* ELSE */ - "switch statement", /* SWITCH */ - "with statement", /* WITH */ - "do loop", /* DO_LOOP */ - "for loop", /* FOR_LOOP */ - "for/in loop", /* FOR_IN_LOOP */ - "while loop", /* WHILE_LOOP */ + "block", /* BLOCK */ + "label statement", /* LABEL */ + "if statement", /* IF */ + "else statement", /* ELSE */ + "switch statement", /* SWITCH */ + "with statement", /* WITH */ + "try statement", /* TRY */ + "catch block", /* CATCH */ + "finally statement", /* FINALLY */ + "do loop", /* DO_LOOP */ + "for loop", /* FOR_LOOP */ + "for/in loop", /* FOR_IN_LOOP */ + "while loop", /* WHILE_LOOP */ }; static const char * StatementName(JSCodeGenerator *cg) { - if (!cg || !cg->stmtInfo) + if (!cg->treeContext.topStmt) return "script"; - return statementName[cg->stmtInfo->type]; + return statementName[cg->treeContext.topStmt->type]; } static void @@ -257,86 +216,40 @@ js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, return JS_TRUE; } -/* Forward declaration to keep MoveGotos after PatchGotos. */ -static void -MoveGotos(JSCodeGenerator *cg, StmtInfo *stmt, ptrdiff_t last, ptrdiff_t first, - ptrdiff_t growth); - -ptrdiff_t -js_MoveCode(JSContext *cx, - JSCodeGenerator *from, ptrdiff_t fromOffset, - JSCodeGenerator *to, ptrdiff_t toOffset) -{ - ptrdiff_t length, growth, offset, saveOffset; - StmtInfo *stmt; - - length = CG_OFFSET(from) - fromOffset; - growth = (toOffset + length) - CG_OFFSET(to); - saveOffset = to->lastCodeOffset; - offset = EmitCheck(cx, to, *CG_CODE(from, from->lastCodeOffset), growth); - if (offset >= 0) { - memmove(CG_CODE(to, toOffset), CG_CODE(from, fromOffset), length); - to->ptr += growth; - if (to == from) { - growth = toOffset - fromOffset; - for (stmt = to->stmtInfo; stmt; stmt = stmt->down) { - if (stmt->top >= fromOffset) - stmt->top += growth; - if (stmt->update >= fromOffset) - stmt->update += growth; - if (stmt->breaks >= fromOffset) { - stmt->breaks += growth; - MoveGotos(to, stmt, stmt->breaks, toOffset, growth); - } - if (stmt->continues >= fromOffset) { - stmt->continues += growth; - MoveGotos(to, stmt, stmt->continues, toOffset, growth); - } - } - to->lastCodeOffset = saveOffset + growth; - } else { - /* For now, we handle expressions only, not statements. */ - PR_ASSERT(!from->stmtInfo); - to->lastCodeOffset += from->lastCodeOffset - fromOffset; - if (from->maxStackDepth > to->maxStackDepth) - to->maxStackDepth = from->maxStackDepth; - } - } - return offset; -} - void -js_PushStatement(JSCodeGenerator *cg, StmtInfo *stmt, StmtType type, +js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, ptrdiff_t top) { stmt->type = type; SET_STATEMENT_TOP(stmt, top); stmt->label = NULL; - stmt->down = cg->stmtInfo; - cg->stmtInfo = stmt; + stmt->down = tc->topStmt; + tc->topStmt = stmt; } static ptrdiff_t -EmitGoto(JSContext *cx, JSCodeGenerator *cg, StmtInfo *toStmt, - ptrdiff_t *last, JSAtom *label, JSSrcNoteType noteType) +EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, + ptrdiff_t *last, JSAtomListElement *label, JSSrcNoteType noteType) { - StmtInfo *stmt; + JSStmtInfo *stmt; intN index; ptrdiff_t offset, delta; - for (stmt = cg->stmtInfo; stmt != toStmt; stmt = stmt->down) { + for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { switch (stmt->type) { case STMT_WITH: - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) { + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return -1; + cg->stackDepth++; + if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) return -1; - } break; case STMT_FOR_IN_LOOP: - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_POP) < 0) { + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return -1; + cg->stackDepth += 2; + if (js_Emit1(cx, cg, JSOP_POP2) < 0) return -1; - } break; default:; } @@ -360,8 +273,8 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, StmtInfo *toStmt, } static JSBool -PatchGotos(JSContext *cx, JSCodeGenerator *cg, StmtInfo *stmt, ptrdiff_t last, - jsbytecode *target) +PatchGotos(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt, + ptrdiff_t last, jsbytecode *target) { jsbytecode *pc, *top; ptrdiff_t delta, jumpOffset; @@ -378,58 +291,1509 @@ PatchGotos(JSContext *cx, JSCodeGenerator *cg, StmtInfo *stmt, ptrdiff_t last, return JS_TRUE; } -static void -MoveGotos(JSCodeGenerator *cg, StmtInfo *stmt, ptrdiff_t last, ptrdiff_t first, - ptrdiff_t growth) -{ - jsbytecode *pc, *top, *stop, *prev; - ptrdiff_t delta; - - pc = CG_CODE(cg, last); - top = CG_CODE(cg, stmt->top); - stop = CG_CODE(cg, first); - while (pc > top) { - PR_ASSERT(*pc == JSOP_GOTO); - delta = GET_JUMP_OFFSET(pc); - prev = pc - delta; - if (prev < stop) { - delta += growth; - SET_JUMP_OFFSET(pc, delta); - return; - } - pc = prev; - } -} - ptrdiff_t -js_EmitBreak(JSContext *cx, JSCodeGenerator *cg, StmtInfo *stmt, - JSAtom *label) +js_EmitBreak(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt, + JSAtomListElement *label) { return EmitGoto(cx, cg, stmt, &stmt->breaks, label, SRC_BREAK2LABEL); } ptrdiff_t -js_EmitContinue(JSContext *cx, JSCodeGenerator *cg, StmtInfo *stmt, - JSAtom *label) +js_EmitContinue(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt, + JSAtomListElement *label) { return EmitGoto(cx, cg, stmt, &stmt->continues, label, SRC_CONT2LABEL); } -JSBool -js_PopStatement(JSContext *cx, JSCodeGenerator *cg) +extern void +js_PopStatement(JSTreeContext *tc) { - StmtInfo *stmt; + tc->topStmt = tc->topStmt->down; +} - stmt = cg->stmtInfo; - if (!PatchGotos(cx, cg, stmt, stmt->breaks, cg->ptr)) +JSBool +js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg) +{ + JSStmtInfo *stmt; + + stmt = cg->treeContext.topStmt; + if (!PatchGotos(cx, cg, stmt, stmt->breaks, cg->next)) return JS_FALSE; if (!PatchGotos(cx, cg, stmt, stmt->continues, CG_CODE(cg, stmt->update))) return JS_FALSE; - cg->stmtInfo = stmt->down; + cg->treeContext.topStmt = stmt->down; return JS_TRUE; } -const char *js_SrcNoteName[] = { +/* + * Emit a bytecode and its 2-byte constant (atom) index immediate operand. + * NB: We use cx and cg from our caller's lexical environment, and return + * false on error. + */ +#define EMIT_ATOM_INDEX_OP(op, atomIndex) \ + PR_BEGIN_MACRO \ + if (js_Emit3(cx, cg, op, ATOM_INDEX_HI(atomIndex), \ + ATOM_INDEX_LO(atomIndex)) < 0) { \ + return JS_FALSE; \ + } \ + PR_END_MACRO + +static JSBool +EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + JSAtomListElement *ale; + + ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(op, ale->index); + return JS_TRUE; +} + +static JSBool +EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + JSParseNode *pn2; + JSAtomListElement *ale; + + pn2 = pn->pn_expr; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, + (ptrdiff_t)(CG_OFFSET(cg) - pn2->pn_offset)) < 0) { + return JS_FALSE; + } + if (!pn->pn_atom) { + PR_ASSERT(op == JSOP_IMPORTALL); + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } else { + ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(op, ale->index); + } + return JS_TRUE; +} + +static JSBool +EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + JSParseNode *pn2; + + pn2 = pn->pn_left; + if (!js_EmitTree(cx, cg, pn2) || !js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, + (ptrdiff_t)(CG_OFFSET(cg) - pn2->pn_offset)) < 0) { + return JS_FALSE; + } + return js_Emit1(cx, cg, op) >= 0; +} + +static JSBool +EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) +{ + jsint ival; + jsatomid atomIndex; + 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; + if (ival == 1) + return js_Emit1(cx, cg, JSOP_ONE) >= 0; + if ((jsuint)ival < (jsuint)ATOM_INDEX_LIMIT) { + atomIndex = (jsatomid)ival; + EMIT_ATOM_INDEX_OP(JSOP_UINT16, atomIndex); + return JS_TRUE; + } + atom = js_AtomizeInt(cx, ival, 0); + } else { + atom = js_AtomizeDouble(cx, dval, 0); + } + if (!atom) + return JS_FALSE; + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_NUMBER, ale->index); + return JS_TRUE; +} + +JSBool +js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, + JSFunction *fun) +{ + if (!js_AllocTryNotes(cx, cg)) + return JS_FALSE; + if (!js_EmitTree(cx, cg, body)) + return JS_FALSE; + fun->script = js_NewScriptFromCG(cx, cg, fun); + if (!fun->script) + return JS_FALSE; + return JS_TRUE; +} + +JSBool +js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) +{ + JSBool ok; + uintN lineno, delta; + JSCodeGenerator cg2; + JSStmtInfo *stmt, stmtInfo; + ptrdiff_t top, off, tmp, beq, jmp; + JSParseNode *pn2, *pn3, *pn4; + JSAtom *atom; + JSAtomListElement *ale; + jsatomid atomIndex; + intN noteIndex; + JSOp op; + uint32 argc; + + pn->pn_offset = top = CG_OFFSET(cg); + + /* Emit notes to tell the current bytecode's source line number. */ + lineno = pn->pn_pos.begin.lineno; + 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))) { + if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)lineno) < 0) + return JS_FALSE; + } else { + do { + if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0) + return JS_FALSE; + } while (--delta != 0); + } + } + + switch (pn->pn_type) { + case TOK_FUNCTION: + { + JSFunction *fun; + + 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) + return JS_FALSE; + + /* Make the function object a literal in the outer script's pool. */ + atom = js_AtomizeObject(cx, fun->object, 0); + if (!atom) + return JS_FALSE; + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + + /* Emit a bytecode or srcnote naming the literal in its immediate. */ +#if JS_HAS_LEXICAL_CLOSURE + if (pn->pn_op != JSOP_NOP) { + EMIT_ATOM_INDEX_OP(pn->pn_op, ale->index); + break; + } +#endif + noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)ale->index); + if (noteIndex < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + break; + } + +#if JS_HAS_EXPORT_IMPORT + case TOK_EXPORT: + pn2 = pn->pn_head; + if (pn2->pn_type == TOK_STAR) { + /* + * 'export *' must have no other elements in the list (what would + * be the point?). + */ + if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0) + return JS_FALSE; + } else { + /* + * If not 'export *', the list consists of NAME nodes identifying + * properties of the variable object to flag as exported. + */ + do { + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ale->index); + } while ((pn2 = pn2->pn_next) != NULL); + } + break; + + case TOK_IMPORT: + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + /* + * Each subtree on an import list is rooted by a DOT or LB node. + * A DOT may have a null pn_atom member, in which case pn_op must + * be JSOP_IMPORTALL -- see EmitPropOp above. + */ + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + break; +#endif /* JS_HAS_EXPORT_IMPORT */ + + case TOK_IF: + /* Emit code for the condition before pushing stmtInfo. */ + if (!js_EmitTree(cx, cg, pn->pn_kid1)) + return JS_FALSE; + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, CG_OFFSET(cg)); + + /* Emit an annotated branch-if-false around the then part. */ + noteIndex = js_NewSrcNote(cx, cg, SRC_IF); + if (noteIndex < 0) + return JS_FALSE; + beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); + if (beq < 0) + return JS_FALSE; + + /* Emit code for the then and optional else parts. */ + if (!js_EmitTree(cx, cg, pn->pn_kid2)) + return JS_FALSE; + pn3 = pn->pn_kid3; + if (pn3) { + /* Modify stmtInfo and the branch-if-false source note. */ + stmtInfo.type = STMT_ELSE; + SN_SET_TYPE(&cg->notes[noteIndex], SRC_IF_ELSE); + + /* Jump at end of then part around the else part. */ + jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); + if (jmp < 0) + return JS_FALSE; + + /* Ensure the branch-if-false comes here, then emit the else. */ + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + + /* Fixup the jump around the else part. */ + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + } else { + /* No else part, fixup the branch-if-false to come here. */ + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + } + return js_PopStatementCG(cx, cg); + +#if JS_HAS_SWITCH_STATEMENT + case TOK_SWITCH: + { + JSOp switchop; + uint32 ncases, tablen; + JSScript *script; + jsint i, low, high; + size_t switchsize, tablesize; + void *mark; + JSParseNode **table; + jsbytecode *pc; + + /* Emit code for the discriminant first. */ + if (!js_EmitTree(cx, cg, pn->pn_kid1)) + return JS_FALSE; + + /* Switch bytecodes run from here till end of final case. */ + top = CG_OFFSET(cg); + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_SWITCH, top); + + switchop = JSOP_TABLESWITCH; + pn2 = pn->pn_kid2; + ncases = pn2->pn_count; + + if (pn2->pn_count == 0) { + low = high = 0; + tablen = 0; + ok = JS_TRUE; + } else { + low = JSVAL_INT_MAX; + high = JSVAL_INT_MIN; + cg2.base = NULL; + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + PR_ASSERT(pn3->pn_type == TOK_CASE); + if (!cg2.base) { + if (!js_InitCodeGenerator(cx, &cg2, cg->filename, + pn3->pn_pos.begin.lineno, + cg->principals)) { + return JS_FALSE; + } + } else { + js_ResetCodeGenerator(cx, &cg2); + } + pn4 = pn3->pn_left; + cg2.currentLine = pn4->pn_pos.begin.lineno; + if (!js_EmitTree(cx, &cg2, pn4)) + return JS_FALSE; + if (js_Emit1(cx, &cg2, JSOP_POPV) < 0) + return JS_FALSE; + script = js_NewScriptFromCG(cx, &cg2, NULL); + if (!script) + return JS_FALSE; + + /* + * The GC must not run during this case expression evaluation. + * It won't if it runs only from the branch callback (because + * the only branches possible in a case expression come from ?: + * expressions, and those generate downward branches, while the + * branch callback runs only for upward branches). + */ + ok = js_Execute(cx, cx->fp->scopeChain, script, NULL, cx->fp, + JS_FALSE, &pn3->pn_val); + js_DestroyScript(cx, script); + if (!ok) + return JS_FALSE; + + if (!JSVAL_IS_NUMBER(pn3->pn_val) && + !JSVAL_IS_STRING(pn3->pn_val) && + !JSVAL_IS_BOOLEAN(pn3->pn_val)) { + JS_ReportError(cx, + "%s, line %u: invalid case expression", + cg2.filename ? cg2.filename : "stdin", + pn4->pn_pos.begin.lineno); + return JS_FALSE; + } + + if (switchop == JSOP_LOOKUPSWITCH) + continue; + if (!JSVAL_IS_INT(pn3->pn_val)) { + switchop = JSOP_LOOKUPSWITCH; + continue; + } + i = JSVAL_TO_INT(pn3->pn_val); + if ((jsuint)(i + (jsint)PR_BIT(15)) >= (jsuint)PR_BIT(16)) { + switchop = JSOP_LOOKUPSWITCH; + continue; + } + if (i < low) + low = i; + if (high < i) + high = i; + } + if (cg2.base) + js_ResetCodeGenerator(cx, &cg2); + if (switchop == JSOP_TABLESWITCH) { + tablen = (uint32)(high - low + 1); + if (tablen >= PR_BIT(16) || tablen > 2 * ncases) + switchop = JSOP_LOOKUPSWITCH; + } + } + + /* Emit a note with an offset telling total switch code length. */ + noteIndex = js_NewSrcNote2(cx, cg, SRC_SWITCH, 0); + if (noteIndex < 0) + return JS_FALSE; + + if (switchop == JSOP_TABLESWITCH) { + /* + * 3 offsets (len, low, high) before the table, 1 per entry. + */ + switchsize = (size_t)(6 + 2 * tablen); + } else { + /* + * 1 offset (len) and 1 atom index (npairs) before the table, + * 1 atom index and 1 jump offset per entry. + */ + switchsize = (size_t)(4 + 4 * ncases); + } + + /* Emit switchop and switchsize bytes of jump or lookup table. */ + if (js_EmitN(cx, cg, switchop, switchsize) < 0) + return JS_FALSE; + + /* Emit code for each case's statements, copying pn_offset up to pn3. */ + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + pn4 = pn3->pn_right; + if (!js_EmitTree(cx, cg, pn4)) + return JS_FALSE; + pn3->pn_offset = pn4->pn_offset; + } + + /* Emit the default case (if any) and compute default offset. */ + pn3 = pn->pn_kid3; + if (pn3) { + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + off = pn3->pn_offset - top; + } else { + off = CG_OFFSET(cg) - top; + } + + /* Set the default offset (to end of switch if no default). */ + pc = CG_CODE(cg, top); + if (!js_SetJumpOffset(cx, cg, pc, off)) + return JS_FALSE; + pc += 2; + + /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ + off = CG_OFFSET(cg) - top; + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, (ptrdiff_t)off)) + return JS_FALSE; + + /* Fill in jump or lookup table. */ + if (switchop == JSOP_TABLESWITCH) { + if (!js_SetJumpOffset(cx, cg, pc, low)) + return JS_FALSE; + pc += 2; + if (!js_SetJumpOffset(cx, cg, pc, high)) + return JS_FALSE; + pc += 2; + if (tablen) { + /* Avoid bloat for a compilation unit with many switches. */ + mark = PR_ARENA_MARK(&cx->tempPool); + tablesize = (size_t)tablen * sizeof *table; + PR_ARENA_ALLOCATE(table, &cx->tempPool, tablesize); + if (!table) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + memset(table, 0, tablesize); + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + i = JSVAL_TO_INT(pn3->pn_val); + i -= low; + PR_ASSERT((uint32)i < tablen); + table[i] = pn3; + } + for (i = 0; i < (jsint)tablen; i++) { + pn3 = table[i]; + off = pn3 ? pn3->pn_offset - top : 0; + if (!js_SetJumpOffset(cx, cg, pc, off)) + return JS_FALSE; + pc += 2; + } + PR_ARENA_RELEASE(&cx->tempPool, mark); + } + } else { + SET_ATOM_INDEX(pc, ncases); + pc += 2; + + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + atom = js_AtomizeValue(cx, pn3->pn_val, 0); + if (!atom) + return JS_FALSE; + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + SET_ATOM_INDEX(pc, ale->index); + pc += 2; + + off = pn3->pn_offset - top; + if (!js_SetJumpOffset(cx, cg, pc, off)) + return JS_FALSE; + pc += 2; + } + } + + return js_PopStatementCG(cx, cg); + } +#endif /* JS_HAS_SWITCH_STATEMENT */ + + case TOK_WHILE: + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top); + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); + if (noteIndex < 0) + return JS_FALSE; + beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); + if (beq < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); + if (jmp < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,jmp), top - jmp); + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + return js_PopStatementCG(cx, cg); + +#if JS_HAS_DO_WHILE_LOOP + case TOK_DO: + /* Emit an annotated nop so we know to decompile a 'do' keyword. */ + if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + + /* Compile the loop body. */ + top = CG_OFFSET(cg); + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top); + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + + /* Set loop and enclosing label update offsets, for continue. */ + stmt = &stmtInfo; + do { + stmt->update = CG_OFFSET(cg); + } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); + + /* Compile the loop condition, now that continues know where to go. */ + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + + /* Re-use the SRC_WHILE note, this time for the JSOP_IFNE opcode. */ + if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0) + return JS_FALSE; + jmp = top - CG_OFFSET(cg); + if (js_Emit3(cx, cg, JSOP_IFNE, + JUMP_OFFSET_HI(jmp), JUMP_OFFSET_LO(jmp)) < 0) { + return JS_FALSE; + } + return js_PopStatementCG(cx, cg); +#endif /* JS_HAS_DO_WHILE_LOOP */ + + case TOK_FOR: + pn2 = pn->pn_left; + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top); + + if (pn2->pn_type == TOK_IN) { + /* If the left part is var x = i, bind x, evaluate i, and pop. */ + pn3 = pn2->pn_left; + if (pn3->pn_type == TOK_VAR && pn3->pn_head->pn_expr) { + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + /* Set pn3 to the variable name, to avoid another var note. */ + pn3 = pn3->pn_head; + PR_ASSERT(pn3->pn_type == TOK_NAME); + } + + /* Fix stmtInfo and emit a push to allocate the iterator. */ + stmtInfo.type = STMT_FOR_IN_LOOP; + noteIndex = -1; + if (js_Emit1(cx, cg, JSOP_PUSH) < 0) + return JS_FALSE; + + /* Compile the object expression to the right of 'in'. */ + if (!js_EmitTree(cx, cg, pn2->pn_right)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_TOOBJECT) < 0) + return JS_FALSE; + + top = CG_OFFSET(cg); + SET_STATEMENT_TOP(&stmtInfo, top); + + /* Compile a JSOP_FOR*2 bytecode based on the left hand side. */ + switch (pn3->pn_type) { + case TOK_VAR: + pn3 = pn3->pn_head; + if (js_NewSrcNote(cx, cg, SRC_VAR) < 0) + return JS_FALSE; + /* FALL THROUGH */ + case TOK_NAME: + if (!EmitAtomOp(cx, pn3, JSOP_FORNAME2, cg)) + return JS_FALSE; + break; + case TOK_DOT: + if (!EmitPropOp(cx, pn3, JSOP_FORPROP2, cg)) + return JS_FALSE; + break; + case TOK_LB: + if (!EmitElemOp(cx, pn3, JSOP_FORELEM2, cg)) + return JS_FALSE; + break; + default: + PR_ASSERT(0); + } + + /* Pop and test the loop condition generated by JSOP_FOR*2. */ + beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); + if (beq < 0) + return JS_FALSE; + } else { + if (!pn2->pn_kid1) { + /* No initializer: emit an annotated nop for the decompiler. */ + noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); + if (noteIndex < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + } else { + if (!js_EmitTree(cx, cg, pn2->pn_kid1)) + return JS_FALSE; + noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); + if (noteIndex < 0 || + js_Emit1(cx, cg, JSOP_POP) < 0) { + return JS_FALSE; + } + } + + top = CG_OFFSET(cg); + SET_STATEMENT_TOP(&stmtInfo, top); + if (!pn2->pn_kid2) { + /* No loop condition: flag this fact in the source notes. */ + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, 0)) + return JS_FALSE; + beq = 0; + } else { + if (!js_EmitTree(cx, cg, pn2->pn_kid2)) + return JS_FALSE; + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, + (ptrdiff_t)(CG_OFFSET(cg) - top))) { + return JS_FALSE; + } + beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); + if (beq < 0) + return JS_FALSE; + } + } + + /* Emit code for the loop body. */ + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + + if (pn2->pn_type != TOK_IN) { + /* Set the second note offset so we can find the update part. */ + PR_ASSERT(noteIndex != -1); + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, + (ptrdiff_t)(CG_OFFSET(cg) - top))) { + return JS_FALSE; + } + + pn3 = pn2->pn_kid3; + if (pn3) { + /* Set loop and enclosing "update" offsets, for continue. */ + stmt = &stmtInfo; + do { + stmt->update = CG_OFFSET(cg); + } while ((stmt = stmt->down) != NULL && + stmt->type == STMT_LABEL); + + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + + /* Restore the absolute line number for source note readers. */ + off = (ptrdiff_t) pn->pn_pos.end.lineno; + if (cg->currentLine != (uintN) off) { + if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0) + return JS_FALSE; + cg->currentLine = (uintN) off; + } + } + + /* The third note offset helps us find the loop-closing jump. */ + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 2, + (ptrdiff_t)(CG_OFFSET(cg) - top))) { + return JS_FALSE; + } + } + + /* Emit the loop-closing jump and fixup all jump offsets. */ + jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); + if (jmp < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,jmp), top - jmp); + if (beq > 0) + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + + /* Now fixup all breaks and continues (before for/in's final POP2). */ + if (!js_PopStatementCG(cx, cg)) + return JS_FALSE; + + if (pn2->pn_type == TOK_IN) { + /* + * Generate the object and iterator pop opcodes after popping the + * stmtInfo stack, so breaks will go to this pop bytecode. + */ + if (js_Emit1(cx, cg, JSOP_POP2) < 0) + return JS_FALSE; + } + break; + + case TOK_BREAK: + stmt = cg->treeContext.topStmt; + atom = pn->pn_atom; + if (atom) { + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + while (stmt->type != STMT_LABEL || stmt->label != atom) + stmt = stmt->down; + } else { + ale = NULL; + while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH) + stmt = stmt->down; + } + if (js_EmitBreak(cx, cg, stmt, ale) < 0) + return JS_FALSE; + break; + + case TOK_CONTINUE: + stmt = cg->treeContext.topStmt; + atom = pn->pn_atom; + if (atom) { + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + while (stmt->type != STMT_LABEL || stmt->label != atom) + stmt = stmt->down; + } else { + ale = NULL; + while (!STMT_IS_LOOP(stmt)) + stmt = stmt->down; + if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) + return JS_FALSE; + } + if (js_EmitContinue(cx, cg, stmt, ale) < 0) + return JS_FALSE; + break; + + case TOK_WITH: + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); + if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) + return JS_FALSE; + return js_PopStatementCG(cx, cg); + +#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 */ + if (pn->pn_kid3) + js_PushStatement(&cg->treeContext, &stmtInto, STMT_FINALLY, + CG_OFFSET(cg)); +#endif + + /* mark try location for decompilation, then emit try block */ + if (js_NewSrcNote(cx, cg, SRC_TRY) < 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 */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); + if (jmp < 0) + 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)) + 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)) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_kid3)) + 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; + + /* + * 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)) + 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: + noteIndex = -1; + for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { + PR_ASSERT(pn2->pn_type == TOK_NAME); + op = pn2->pn_op; + if (pn2->pn_slot >= 0) { + atomIndex = (jsatomid) pn2->pn_slot; + } else { + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ale->index; + } + if (pn2->pn_expr) { + if (op == JSOP_SETNAME2) + EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); + if (!js_EmitTree(cx, cg, pn2->pn_expr)) + return JS_FALSE; + } + if (pn2 == pn->pn_head && js_NewSrcNote(cx, cg, SRC_VAR) < 0) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(op, atomIndex); + tmp = CG_OFFSET(cg); + if (noteIndex >= 0) { + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, tmp - off)) + return JS_FALSE; + } + if (!pn2->pn_next) + break; + off = tmp; + noteIndex = js_NewSrcNote2(cx, cg, SRC_COMMA, 0); + if (noteIndex < 0 || + js_Emit1(cx, cg, JSOP_POP) < 0) { + return JS_FALSE; + } + } + if (pn->pn_op != JSOP_NOP) { + if (js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + } + break; + + case TOK_RETURN: + pn2 = pn->pn_kid; + if (pn2) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } else { + if (js_Emit1(cx, cg, JSOP_PUSH) < 0) + return JS_FALSE; + } + if (js_Emit1(cx, cg, JSOP_RETURN) < 0) + return JS_FALSE; + break; + + case TOK_LC: + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top); + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + return js_PopStatementCG(cx, cg); + + case TOK_SEMI: + if (pn->pn_kid) { + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POPV) < 0) + return JS_FALSE; + } + break; + + case TOK_COLON: + /* Emit an annotated nop so we know to decompile a label. */ + atom = pn->pn_atom; + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + pn2 = pn->pn_expr; + noteIndex = js_NewSrcNote2(cx, cg, + (pn2->pn_type == TOK_LC) + ? SRC_LABELBRACE + : SRC_LABEL, + (ptrdiff_t)ale->index); + if (noteIndex < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + + /* Emit code for the labeled statement. */ + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL, CG_OFFSET(cg)); + stmtInfo.label = atom; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (!js_PopStatementCG(cx, cg)) + return JS_FALSE; + + /* If the statement was compound, emit a note for the end brace. */ + if (pn2->pn_type == TOK_LC) { + if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + } + break; + + case TOK_COMMA: + /* + * Emit SRC_COMMA notes on each JSOP_POP between comma operands. + * These notes help the decompiler bracket the bytecodes generated + * from each sub-expression that follows a comma. + */ + noteIndex = -1; + for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + tmp = CG_OFFSET(cg); + if (noteIndex >= 0) { + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, tmp - off)) + return JS_FALSE; + } + if (!pn2->pn_next) + break; + off = tmp; + noteIndex = js_NewSrcNote2(cx, cg, SRC_COMMA, 0); + if (noteIndex < 0 || + js_Emit1(cx, cg, JSOP_POP) < 0) { + return JS_FALSE; + } + } + break; + + case TOK_ASSIGN: + /* + * Check left operand type and generate specialized code for it. + * Specialize to avoid ECMA "reference type" values on the operand + * stack, which impose pervasive runtime "GetValue" costs. + */ + pn2 = pn->pn_left; + PR_ASSERT(pn2->pn_type != TOK_RP); + switch (pn2->pn_type) { + case TOK_NAME: + if (pn2->pn_slot >= 0) { + atomIndex = (jsatomid) pn2->pn_slot; + } else { + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ale->index; + EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); + } + break; + case TOK_DOT: + if (!js_EmitTree(cx, cg, pn2->pn_expr)) + return JS_FALSE; + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ale->index; + break; + case TOK_LB: + if (!js_EmitTree(cx, cg, pn2->pn_left)) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn2->pn_right)) + return JS_FALSE; + break; + default: + PR_ASSERT(0); + } + + /* If += or similar, dup the left operand and get its value. */ + op = pn->pn_op; + if (op != JSOP_NOP) { + switch (pn2->pn_type) { + case TOK_NAME: + if (pn2->pn_op != JSOP_SETNAME2) { + EMIT_ATOM_INDEX_OP((pn2->pn_op == JSOP_SETARG) + ? JSOP_GETARG + : JSOP_GETVAR, + atomIndex); + break; + } + /* FALL THROUGH */ + case TOK_DOT: + if (js_Emit1(cx, cg, JSOP_DUP) < 0) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_GETPROP, atomIndex); + break; + case TOK_LB: + if (js_Emit1(cx, cg, JSOP_DUP2) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) + return JS_FALSE; + break; + default:; + } + } + + /* Now emit the right operand (it may affect the namespace). */ + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + + /* If += etc., emit the binary operator with a decompiler note. */ + if (op != JSOP_NOP) { + if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0 || + js_Emit1(cx, cg, op) < 0) { + return JS_FALSE; + } + } + + /* Left parts such as a.b.c and a[b].c need a decompiler note. */ + if (pn2->pn_type != TOK_NAME) { + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, + (ptrdiff_t)(CG_OFFSET(cg) - top)) < 0) { + return JS_FALSE; + } + } + + /* Finally, emit the specialized assignment bytecode. */ + switch (pn2->pn_type) { + case TOK_NAME: + case TOK_DOT: + EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex); + break; + case TOK_LB: + if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) + return JS_FALSE; + break; + default:; + } + break; + + case TOK_HOOK: + /* Emit the condition, then branch if false to the else part. */ + if (!js_EmitTree(cx, cg, pn->pn_kid1)) + return JS_FALSE; + if (js_NewSrcNote(cx, cg, SRC_COND) < 0) + return JS_FALSE; + beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); + if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2)) + return JS_FALSE; + + /* Jump around else, fixup the branch, emit else, fixup jump. */ + jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); + if (jmp < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + if (!js_EmitTree(cx, cg, pn->pn_kid3)) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + break; + + case TOK_OR: + /* Emit left operand, emit pop-if-converts-to-false-else-jump. */ + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; +#if JS_BUG_SHORT_CIRCUIT + beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); + tmp = js_Emit1(cx, cg, JSOP_TRUE); + jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); + if (beq < 0 || tmp < 0 || jmp < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); +#else + /* + * JSOP_OR converts the operand on the stack to boolean, and if true, + * leaves the original operand value on the stack and jumps; otherwise + * it pops and falls into the next bytecode. + */ + jmp = js_Emit3(cx, cg, JSOP_OR, 0, 0); + if (jmp < 0) + return JS_FALSE; +#endif + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + break; + + case TOK_AND: + /* && is like || except it uses a pop-if-converts-to-true-else-jump. */ + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; +#if JS_BUG_SHORT_CIRCUIT + beq = js_Emit3(cx, cg, JSOP_IFNE, 0, 0); + tmp = js_Emit1(cx, cg, JSOP_FALSE); + jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); + if (beq < 0 || tmp < 0 || jmp < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); +#else + jmp = js_Emit3(cx, cg, JSOP_AND, 0, 0); + if (jmp < 0) + return JS_FALSE; +#endif + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + break; + + case TOK_BITOR: + case TOK_BITXOR: + case TOK_BITAND: + case TOK_EQOP: + case TOK_RELOP: +#if JS_HAS_IN_OPERATOR + case TOK_IN: +#endif +#if JS_HAS_INSTANCEOF + case TOK_INSTANCEOF: +#endif + case TOK_SHOP: + case TOK_PLUS: + case TOK_MINUS: + case TOK_STAR: + case TOK_DIVOP: + /* Binary operators that evaluate both operands unconditionally. */ + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + if (js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + break; + +#if JS_HAS_EXCEPTIONS + case TOK_THROW: +#endif + case TOK_UNARYOP: + /* Unary op, including unary +/-. */ + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + break; + + case TOK_INC: + case TOK_DEC: + /* Emit lvalue-specialized code for ++/-- operators. */ + pn2 = pn->pn_kid; + PR_ASSERT(pn2->pn_type != TOK_RP); + op = pn->pn_op; + switch (pn2->pn_type) { + case TOK_NAME: + if (pn->pn_num >= 0) { + atomIndex = (jsatomid) pn->pn_num; + EMIT_ATOM_INDEX_OP(op, atomIndex); + } else { + if (!EmitAtomOp(cx, pn2, op, cg)) + return JS_FALSE; + } + break; + case TOK_DOT: + if (!EmitPropOp(cx, pn2, op, cg)) + return JS_FALSE; + break; + case TOK_LB: + if (!EmitElemOp(cx, pn2, op, cg)) + return JS_FALSE; + break; + default: + PR_ASSERT(0); + } + break; + + case TOK_NEW: + /* Code for (new f()) and f() are the same, except for the opcode. */ + op = JSOP_NEW; + goto emit_call; + + case TOK_DELETE: + /* Delete is also lvalue-specialized to avoid reference types. */ + pn2 = pn->pn_kid; + PR_ASSERT(pn2->pn_type != TOK_RP); + switch (pn2->pn_type) { + case TOK_NAME: + if (!EmitAtomOp(cx, pn2, JSOP_DELNAME, cg)) + return JS_FALSE; + break; + case TOK_DOT: + if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg)) + return JS_FALSE; + break; + case TOK_LB: + if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg)) + return JS_FALSE; + break; + default: + PR_ASSERT(0); + } + break; + + case TOK_DOT: + /* + * Pop a stack operand, convert it to object, get a property named by + * this bytecode's immediate-indexed atom operand, and push its value + * (not a reference to it). This bytecode sets the virtual machine's + * "obj" register to the left operand's ToObject conversion result, + * for use by JSOP_PUSHOBJ. + */ + return EmitPropOp(cx, pn, pn->pn_op, cg); + + case TOK_LB: + /* + * Pop two operands, convert the left one to object and the right one + * to property name (atom or tagged int), get the named property, and + * push its value. Set the "obj" register to the result of ToObject + * on the left operand. + */ + return EmitElemOp(cx, pn, pn->pn_op, cg); + + case TOK_LP: + /* + * Emit function call or operator new (constructor call) code. First + * emit code for the left operand to evaluate the call- or construct- + * able object expression. + */ + op = JSOP_CALL; + emit_call: + pn2 = pn->pn_head; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + + /* + * Push the virtual machine's "obj" register, which was set by a name, + * property, or element get (or set) bytecode. + */ + if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) + return JS_FALSE; + + /* + * Emit code for each argument in order, then emit the JSOP_CALL or + * JSOP_NEW bytecode with a two-byte immediate telling how many args + * were pushed on the operand stack. + */ + for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + argc = pn->pn_count - 1; + if (js_Emit3(cx, cg, op, ARGC_HI(argc), ARGC_LO(argc)) < 0) + return JS_FALSE; + break; + +#if JS_HAS_INITIALIZERS + case TOK_RB: + /* + * Emit code for [a, b, c] of the form: + * t = new Array; t[0] = a; t[1] = b; t[2] = c; t; + * but use a stack slot for t and avoid dup'ing and popping it via + * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. + */ + ale = js_IndexAtom(cx, cx->runtime->atomState.ArrayAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_NAME, ale->index); + if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) + return JS_FALSE; + + pn2 = pn->pn_head; +#if JS_HAS_SHARP_VARS + if (pn2 && pn2->pn_type == TOK_DEFSHARP) { + EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); + pn2 = pn2->pn_next; + } +#endif + + for (atomIndex = 0; pn2; pn2 = pn2->pn_next) { + /* PrimaryExpr enforced ATOM_INDEX_LIMIT, so in-line optimize. */ + PR_ASSERT(atomIndex < ATOM_INDEX_LIMIT); + if (atomIndex == 0) { + if (js_Emit1(cx, cg, JSOP_ZERO) < 0) + return JS_FALSE; + } else if (atomIndex == 1) { + if (js_Emit1(cx, cg, JSOP_ONE) < 0) + return JS_FALSE; + } else { + EMIT_ATOM_INDEX_OP(JSOP_UINT16, (jsatomid)atomIndex); + } + + /* Sub-optimal: holes in a sparse initializer are void-filled. */ + if (pn2->pn_type == TOK_COMMA) { + if (js_Emit1(cx, cg, JSOP_PUSH) < 0) + return JS_FALSE; + } else { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) + return JS_FALSE; + + atomIndex++; + } + + if (pn->pn_extra) { + /* Emit a source note so we know to decompile an extra comma. */ + if (js_NewSrcNote(cx, cg, SRC_COMMA) < 0) + return JS_FALSE; + } + + /* Emit an op for sharp array cleanup and decompilation. */ + if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) + return JS_FALSE; + break; + + case TOK_RC: + /* + * Emit code for {p:a, '%q':b, 2:c} of the form: + * t = new Object; t.p = a; t['%q'] = b; t[2] = c; t; + * but use a stack slot for t and avoid dup'ing and popping it via + * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. + */ + 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) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) + return JS_FALSE; + + pn2 = pn->pn_head; +#if JS_HAS_SHARP_VARS + if (pn2 && pn2->pn_type == TOK_DEFSHARP) { + EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); + pn2 = pn2->pn_next; + } +#endif + + for (; pn2; pn2 = pn2->pn_next) { + /* Emit an index for t[2], else map an atom for t.p or t['%q']. */ + pn3 = pn2->pn_left; + switch (pn3->pn_type) { + case TOK_NUMBER: + if (!EmitNumberOp(cx, pn3->pn_dval, cg)) + return JS_FALSE; + break; + case TOK_NAME: + case TOK_STRING: + ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + break; + default: + PR_ASSERT(0); + } + + /* Emit code for the property initializer. */ + if (!js_EmitTree(cx, cg, pn2->pn_right)) + return JS_FALSE; + + /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */ + if (pn3->pn_type == TOK_NUMBER) { + if (js_NewSrcNote(cx, cg, SRC_LABEL) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) + return JS_FALSE; + } else { + EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ale->index); + } + } + + /* Emit an op for sharpArray cleanup and decompilation. */ + if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) + return JS_FALSE; + break; + +#if JS_HAS_SHARP_VARS + case TOK_DEFSHARP: + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num); + break; + + case TOK_USESHARP: + EMIT_ATOM_INDEX_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); + break; +#endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ + + case TOK_RP: + /* + * The node for (e) has e as its kid, enabling users who want to nest + * assignment expressions in conditions to avoid the error correction + * done by Condition (from x = y to x == y) by double-parenthesizing. + * + * We also emit an annotated NOP so we can decompile user parentheses, + * but that's just a nicety (the decompiler does not preserve comments + * or white space, and it parenthesizes for correct precedence anyway, + * so this nop nicety should be considered with a cold eye, especially + * if another srcnote type is needed). + */ + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + + if (js_NewSrcNote(cx, cg, SRC_PAREN) < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + break; + + case TOK_NAME: + if (pn->pn_slot >= 0) { + EMIT_ATOM_INDEX_OP(pn->pn_op, (jsatomid) pn->pn_slot); + break; + } + /* FALL THROUGH */ + case TOK_STRING: + case TOK_OBJECT: + /* + * The scanner and parser associate JSOP_NAME with TOK_NAME, although + * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME2, + * JSOP_FORNAME2, etc.). Among JSOP_*NAME* variants, only JSOP_NAME + * may generate the first operand of a call or new expression, so only + * it sets the "obj" virtual machine register to the object along the + * scope chain in which the name was found. + * + * Token types for STRING and OBJECT have corresponding bytecode ops + * in pn_op and emit the same format as NAME, so they share this code. + */ + return EmitAtomOp(cx, pn, pn->pn_op, cg); + + case TOK_NUMBER: + return EmitNumberOp(cx, pn->pn_dval, cg); + + case TOK_PRIMARY: + return js_Emit1(cx, cg, pn->pn_op) >= 0; + + default: + PR_ASSERT(0); + } + + return JS_TRUE; +} + +JS_FRIEND_DATA(const char *) js_SrcNoteName[] = { "null", "if", "if-else", @@ -449,9 +1813,9 @@ const char *js_SrcNoteName[] = { "break2label", "cont2label", "switch", - "unused19", - "unused20", - "unused21", + "funcdef", + "try", + "catch", "newline", "setline", "xdelta" @@ -477,15 +1841,15 @@ uint8 js_SrcNoteArity[] = { 1, /* SRC_BREAK2LABEL */ 1, /* SRC_CONT2LABEL */ 1, /* SRC_SWITCH */ - 0, /* SRC_UNUSED19 */ - 0, /* SRC_UNUSED20 */ - 0, /* SRC_UNUSED21 */ + 1, /* SRC_FUNCDEF */ + 0, /* SRC_TRY */ + 0, /* SRC_CATCH */ 0, /* SRC_NEWLINE */ 1, /* SRC_SETLINE */ 0 /* SRC_XDELTA */ }; -intN +static intN AllocSrcNote(JSContext *cx, JSCodeGenerator *cg) { intN index; @@ -556,17 +1920,6 @@ js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type) if (js_NewSrcNote(cx, cg, SRC_NULL) < 0) return -1; } - - /* - * Set lastNoteCount here, after any recursion, so it has the lowest - * index value among all note bytes allocated by the non-recursive call - * that claimed the first byte. - */ - cg->lastNoteCount = index; - if (type != SRC_NULL && type < SRC_NEWLINE) { - cg->saveNoteCount = index; - cg->saveNoteOffset = saveOffset; - } return index; } @@ -617,35 +1970,6 @@ GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg) return JS_TRUE; } -JSBool -js_BumpSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, uintN index, - ptrdiff_t incr) -{ - jssrcnote *sn; - ptrdiff_t delta, limit, xdelta; - uintN length; - - sn = &cg->notes[index]; - delta = SN_DELTA(sn) + incr; - limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT; - while (delta >= limit) { - length = cg->noteCount - index; - if (AllocSrcNote(cx, cg) < 0) - return JS_FALSE; - sn = &cg->notes[index]; - memmove(sn + 1, sn, length * sizeof(jssrcnote)); - xdelta = PR_MIN(delta, SN_XDELTA_LIMIT - 1); - SN_MAKE_XDELTA(sn, xdelta); - sn++; - delta -= xdelta; - } - SN_SET_DELTA(sn, delta); - cg->lastNoteOffset += incr; - if (index < cg->saveNoteCount) - cg->saveNoteOffset += incr; - return JS_TRUE; -} - uintN js_SrcNoteLength(jssrcnote *sn) { @@ -662,7 +1986,7 @@ js_SrcNoteLength(jssrcnote *sn) return sn - base; } -ptrdiff_t +JS_FRIEND_API(ptrdiff_t) js_GetSrcNoteOffset(jssrcnote *sn, uintN which) { /* Find the offset numbered which (i.e., skip exactly which offsets). */ @@ -719,51 +2043,6 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, return JS_TRUE; } -JSBool -js_MoveSrcNotes(JSContext *cx, JSCodeGenerator *from, JSCodeGenerator *to) -{ - intN i, j, length, index; - jssrcnote *sn; - JSSrcNoteType type; - ptrdiff_t delta, limit, xdelta; - - PR_ASSERT(from != to); - for (i = 0; (uintN)i < from->noteCount; i += length) { - length = SN_LENGTH(&from->notes[i]); - type = SN_TYPE(&from->notes[i]); - index = js_NewSrcNote(cx, to, type); - if (index < 0) - return JS_FALSE; - for (j = (intN)js_SrcNoteArity[type]; --j >= 0; ) { - if (!js_SetSrcNoteOffset(cx, to, index, j, - js_GetSrcNoteOffset(&from->notes[i], j))) { - return JS_FALSE; - } - } - delta = SN_DELTA(&from->notes[i]); - to->lastNoteOffset += delta; - sn = &to->notes[index]; - if (i == 0) { - /* First note's delta must count bytecodes from last note in to. */ - delta += SN_DELTA(sn); - limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT; - if (delta >= limit) { - do { - xdelta = PR_MIN(delta, SN_XDELTA_MASK); - SN_MAKE_XDELTA(sn, xdelta); - delta -= xdelta; - index = js_NewSrcNote(cx, to, SRC_NULL); - if (index < 0) - return JS_FALSE; - sn = &to->notes[index]; - } while (delta >= limit); - } - } - SN_SET_DELTA(sn, delta); - } - return JS_TRUE; -} - JS_FRIEND_API(jssrcnote *) js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg) { @@ -780,3 +2059,60 @@ js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg) CG_RESET_NOTES(cg); return final; } + +JSBool +js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg) +{ + size_t size; + + size = (cg->treeContext.tryCount + 1) * sizeof(JSTryNote); + PR_ARENA_ALLOCATE(cg->tryBase, &cx->tempPool, size); + if (!cg->tryBase) + return JS_FALSE; + cg->tryNext = cg->tryBase; + cg->tryLimit = (JSTryNote *)((char *)cg->tryBase + size); + return JS_TRUE; +} + +JS_FRIEND_API(JSTryNote *) +js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, + ptrdiff_t end, ptrdiff_t catch, ptrdiff_t finally) +{ + JSTryNote *cur; + + 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; +} + +JS_FRIEND_API(JSBool) +js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote **tryp) +{ + uintN count; + JSTryNote *tmp, *final; + + count = cg->tryNext - cg->tryBase; + if (!count) { + *tryp = NULL; + return JS_TRUE; + } + + tmp = cg->tryBase; + final = JS_malloc(cx, (count + 1) * sizeof(JSTryNote)); + if (!final) { + *tryp = NULL; + return JS_FALSE; + } + memcpy(final, tmp, count * sizeof(JSTryNote)); + final[count].start = 0; + final[count].end = 0; + final[count].catch = 0; + final[count].finally = 0; + *tryp = final; + return JS_TRUE; +} diff --git a/mozilla/js/src/jsemit.h b/mozilla/js/src/jsemit.h index 7e196f89ce5..0f020b514a2 100644 --- a/mozilla/js/src/jsemit.h +++ b/mozilla/js/src/jsemit.h @@ -30,87 +30,105 @@ PR_BEGIN_EXTERN_C -typedef enum StmtType { +typedef enum JSStmtType { STMT_BLOCK = 0, /* compound statement: { s1[;... sN] } */ STMT_LABEL = 1, /* labeled statement: l: s */ STMT_IF = 2, /* if (then) statement */ STMT_ELSE = 3, /* else statement */ STMT_SWITCH = 4, /* switch statement */ STMT_WITH = 5, /* with statement */ - STMT_DO_LOOP = 6, /* do/while loop statement */ - STMT_FOR_LOOP = 7, /* for loop statement */ - STMT_FOR_IN_LOOP = 8, /* for/in loop statement */ - STMT_WHILE_LOOP = 9 /* while loop statement */ -} StmtType; + STMT_TRY = 6, /* try statement */ + STMT_CATCH = 7, /* catch block */ + STMT_FINALLY = 8, /* finally statement */ + STMT_DO_LOOP = 9, /* do/while loop statement */ + STMT_FOR_LOOP = 10, /* for loop statement */ + STMT_FOR_IN_LOOP = 11, /* for/in loop statement */ + STMT_WHILE_LOOP = 12 /* while loop statement */ +} JSStmtType; #define STMT_IS_LOOP(stmt) ((stmt)->type >= STMT_DO_LOOP) -typedef struct SwitchCase SwitchCase; -typedef struct StmtInfo StmtInfo; +typedef struct JSStmtInfo JSStmtInfo; -struct StmtInfo { - StmtType type; /* statement type */ - ptrdiff_t top; /* offset of loop top from cg base */ - ptrdiff_t update; /* loop update offset (top if none) */ - ptrdiff_t breaks; /* offset of last break in loop */ - ptrdiff_t continues; /* offset of last continue in loop */ - JSAtom *label; /* label name if type is STMT_LABEL */ - StmtInfo *down; /* info for enclosing statement */ -}; - -struct SwitchCase { - jsval value; - ptrdiff_t offset; - SwitchCase *next; +struct JSStmtInfo { + JSStmtType type; /* statement type */ + ptrdiff_t top; /* offset of loop top from cg base */ + ptrdiff_t update; /* loop update offset (top if none) */ + ptrdiff_t breaks; /* offset of last break in loop */ + ptrdiff_t continues; /* offset of last continue in loop */ + JSAtom *label; /* label name if type is STMT_LABEL */ + JSStmtInfo *down; /* info for enclosing statement */ }; #define SET_STATEMENT_TOP(stmt, top) \ ((stmt)->top = (stmt)->update = (stmt)->breaks = (stmt)->continues = (top)) +struct JSTreeContext { /* tree context for semantic checks */ + uint32 flags; /* statement state flags, see below */ + uint32 tryCount; /* total count of try statements parsed */ + JSStmtInfo *topStmt; /* top of statement info stack */ +}; + +#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 INIT_TREE_CONTEXT(tc) \ + ((tc)->flags = 0, (tc)->tryCount = 0, (tc)->topStmt = NULL) + struct JSCodeGenerator { - PRArenaPool *pool; /* pool in which to allocate code */ - jsbytecode *base; /* base of JS bytecode vector */ - jsbytecode *limit; /* one byte beyond end of bytecode */ - jsbytecode *ptr; /* pointer to next free bytecode */ - JSAtomList atomList; /* literals indexed for mapping */ - JSSymbol *args; /* list of formal argument symbols */ - JSSymbol *vars; /* list of local variable symbols */ - ptrdiff_t lastCodeOffset; /* offset of last non-nop opcode */ - StmtInfo *stmtInfo; /* statement stack for break/continue */ - intN stackDepth; /* current stack depth in basic block */ - uintN maxStackDepth; /* maximum stack depth so far */ - jssrcnote *notes; /* source notes, see below */ - uintN noteCount; /* number of source notes so far */ - uintN lastNoteCount; /* index of last emitted source note */ - uintN saveNoteCount; /* saved index for source note cancellation */ - ptrdiff_t lastNoteOffset; /* code offset of last emitted source note */ - ptrdiff_t saveNoteOffset; /* saved offset before last cancellable note */ + void *codeMark; /* low watermark in cx->codePool */ + void *tempMark; /* low watermark in cx->tempPool */ + jsbytecode *base; /* base of JS bytecode vector */ + jsbytecode *limit; /* one byte beyond end of bytecode */ + jsbytecode *next; /* pointer to next free bytecode */ + JSAtomList atomList; /* literals indexed for mapping */ + ptrdiff_t lastCodeOffset; /* offset of last non-nop opcode */ + intN stackDepth; /* current stack depth in basic block */ + uintN maxStackDepth; /* maximum stack depth so far */ + jssrcnote *notes; /* source notes, see below */ + uintN noteCount; /* number of source notes so far */ + ptrdiff_t lastNoteOffset; /* code offset for last source note */ + const char *filename; /* null or weak link to source filename */ + uintN firstLine; /* first line, for js_NewScriptFromCG */ + uintN currentLine; /* line number for tree-based srcnote gen */ + JSPrincipals *principals; /* principals for constant folding eval */ + JSTreeContext treeContext; /* for break/continue code generation */ + JSTryNote *tryBase; /* first exception handling block */ + JSTryNote *tryNext; /* next avail block */ + JSTryNote *tryLimit; /* pointer to one-past-end block */ }; #define CG_CODE(cg,offset) ((cg)->base + (offset)) -#define CG_OFFSET(cg) ((cg)->ptr - (cg)->base) -#define CG_RESET(cg) ((cg)->ptr = (cg)->base, \ - INIT_ATOM_LIST(&(cg)->atomList), \ - (cg)->args = (cg)->vars = NULL, \ +#define CG_OFFSET(cg) ((cg)->next - (cg)->base) +#define CG_RESET(cg) ((cg)->next = (cg)->base, \ + ATOM_LIST_INIT(&(cg)->atomList), \ (cg)->lastCodeOffset = 0, \ - (cg)->stmtInfo = NULL, \ (cg)->stackDepth = (cg)->maxStackDepth = 0, \ + (cg)->currentLine = (cg)->firstLine, \ + INIT_TREE_CONTEXT(&(cg)->treeContext), \ + (cg)->tryNext = (cg)->tryBase, \ CG_RESET_NOTES(cg)) #define CG_RESET_NOTES(cg) ((cg)->notes = NULL, (cg)->noteCount = 0, \ - (cg)->lastNoteCount = 0, \ - (cg)->saveNoteCount = 0, \ - (cg)->lastNoteOffset = 0, \ - (cg)->saveNoteOffset = 0) + (cg)->lastNoteOffset = 0) #define CG_PUSH(cg, newcg) ((newcg)->atomList = (cg)->atomList) #define CG_POP(cg, newcg) ((cg)->atomList = (newcg)->atomList) /* - * Initialize cg to allocate from an arena pool. Return true on success. - * Report an exception and return false if the initial code segment can't - * be allocated. + * Initialize cg to allocate bytecode space from cx->codePool, and srcnote + * space from cx->tempPool. Return true on success. Report an error and + * return false if the initial code segment can't be allocated. */ extern JS_FRIEND_API(JSBool) -js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, PRArenaPool *pool); +js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, + const char *filename, uintN lineno, + JSPrincipals *principals); + +/* + * Release cx->codePool and cx->tempPool to marks set by js_InitCodeGenerator. + */ +extern JS_FRIEND_API(void) +js_ResetCodeGenerator(JSContext *cx, JSCodeGenerator *cg); /* * Emit one bytecode. @@ -131,6 +149,12 @@ extern ptrdiff_t js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, jsbytecode op2); +/* + * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. + */ +extern ptrdiff_t +js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra); + /* * Unsafe macro to call js_SetJumpOffset and return false if it does. */ @@ -148,56 +172,53 @@ js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, ptrdiff_t off); /* - * Cancel the last bytecode and any immediate operands emitted via cg. - * You can't Cancel more than once between Emit calls. + * Push the C-stack-allocated struct at stmt onto the stmtInfo stack. */ extern void -js_CancelLastOpcode(JSContext *cx, JSCodeGenerator *cg, uint16 *newlinesp); - -/* - * Update cg's stack depth budget for the opcode located at offset in cg. - */ -extern void -js_UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t offset); - -/* - * Copy code from code generator from starting at fromOffset and running to - * CG_OFFSET(from) into code generator to, starting at toOffset. - * - * NB: this function does not move source notes; the caller must do that. - */ -extern ptrdiff_t -js_MoveCode(JSContext *cx, - JSCodeGenerator *from, ptrdiff_t fromOffset, - JSCodeGenerator *to, ptrdiff_t toOffset); - -/* - * Push the stack-allocated struct at stmt onto the cg->stmtInfo stack. - */ -extern void -js_PushStatement(JSCodeGenerator *cg, StmtInfo *stmt, StmtType type, +js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, ptrdiff_t top); /* * Emit a break instruction, recording it for backpatching. */ extern ptrdiff_t -js_EmitBreak(JSContext *cx, JSCodeGenerator *cg, StmtInfo *stmt, - JSAtom *label); +js_EmitBreak(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt, + JSAtomListElement *label); /* * Emit a continue instruction, recording it for backpatching. */ extern ptrdiff_t -js_EmitContinue(JSContext *cx, JSCodeGenerator *cg, StmtInfo *stmt, - JSAtom *label); +js_EmitContinue(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt, + JSAtomListElement *label); /* - * Pop cg->stmtInfo. If the top StmtInfo struct is not stack-allocated, it + * Pop tc->topStmt. If the top JSStmtInfo struct is not stack-allocated, it * is up to the caller to free it. */ +extern void +js_PopStatement(JSTreeContext *tc); + +/* + * Like js_PopStatement(&cg->treeContext), also patch breaks and continues. + * May fail if a jump offset overflows. + */ extern JSBool -js_PopStatement(JSContext *cx, JSCodeGenerator *cg); +js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg); + +/* + * Emit code into cg for the tree rooted at pn. + */ +extern JSBool +js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); + +/* + * Emit code into cg for the tree rooted at body, then create a persistent + * script for fun from cg. + */ +extern JSBool +js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, + JSFunction *fun); /* * Source notes generated along with bytecode for decompiling and debugging. @@ -233,9 +254,9 @@ typedef enum JSSrcNoteType { SRC_BREAK2LABEL = 16, /* JSOP_GOTO for 'break label' with atomid */ SRC_CONT2LABEL = 17, /* JSOP_GOTO for 'continue label' with atomid */ SRC_SWITCH = 18, /* JSOP_*SWITCH with offset to end of switch */ - SRC_UNUSED19 = 19, /* unused */ - SRC_UNUSED20 = 20, /* unused */ - SRC_UNUSED21 = 21, /* unused */ + 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_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 */ @@ -279,9 +300,9 @@ typedef enum JSSrcNoteType { #define SN_2BYTE_OFFSET_FLAG 0x80 #define SN_2BYTE_OFFSET_MASK 0x7f -extern const char *js_SrcNoteName[]; -extern uint8 js_SrcNoteArity[]; -extern uintN js_SrcNoteLength(jssrcnote *sn); +extern JS_FRIEND_DATA(const char *) js_SrcNoteName[]; +extern JS_FRIEND_DATA(uint8) js_SrcNoteArity[]; +extern JS_FRIEND_DATA(uintN) js_SrcNoteLength(jssrcnote *sn); #define SN_LENGTH(sn) ((js_SrcNoteArity[SN_TYPE(sn)] == 0) ? 1 \ : js_SrcNoteLength(sn)) @@ -307,18 +328,10 @@ extern intN js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2); -/* - * Increment the delta of the note at cg->notes[index], overflowing into one - * or more xdelta notes if necessary. - */ -extern JSBool -js_BumpSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, uintN index, - ptrdiff_t incr); - /* * Get and set the offset operand identified by which (0 for the first, etc.). */ -extern ptrdiff_t +extern JS_FRIEND_API(ptrdiff_t) js_GetSrcNoteOffset(jssrcnote *sn, uintN which); extern JSBool @@ -326,20 +339,36 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, uintN which, ptrdiff_t offset); /* - * Copy source notes from one code generator to another. The code annotated - * by the copied notes must be moved *after* the notes are moved. - */ -extern JSBool -js_MoveSrcNotes(JSContext *cx, JSCodeGenerator *from, JSCodeGenerator *to); - -/* - * Finish taking source notes in cx's tempPool by copying them to new stable - * store allocated via JS_malloc. Return null on malloc failure, which means - * this function reported an error. + * Finish taking source notes in cx's tempPool by copying them to new + * stable store allocated via JS_malloc. Return null on malloc failure, + * which means this function reported an error. */ extern JS_FRIEND_API(jssrcnote *) js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg); +/* + * Allocate cg->treeContext.tryCount notes (plus one for the end sentinel) + * from cx->tempPool and set cg->tryBase/tryNext/tryLimit for exactly tryCount + * js_NewTryNote calls. The storage is freed in one fell swoop by JS_Compile* + * API entry points at the end of compilation. + */ +extern JSBool +js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg); + +/* + * Grab the next trynote slot in cg, filling it in appropriately. + */ +extern JS_FRIEND_API(JSTryNote *) +js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, + ptrdiff_t end, ptrdiff_t catch, ptrdiff_t finally); + +/* + * Finish generating exception information, and copy it to JS_malloc + * storage. + */ +extern JS_FRIEND_API(JSBool) +js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote **tryp); + PR_END_EXTERN_C #endif /* jsemit_h___ */ diff --git a/mozilla/js/src/jsfun.c b/mozilla/js/src/jsfun.c index d73f1c22f1f..4fba50dd745 100644 --- a/mozilla/js/src/jsfun.c +++ b/mozilla/js/src/jsfun.c @@ -40,32 +40,256 @@ #include "jsscript.h" #include "jsstr.h" -static char js_arguments_str[] = "arguments"; -static char js_arity_str[] = "arity"; -static char js_callee_str[] = "callee"; -static char js_caller_str[] = "caller"; +enum { + CALL_ARGUMENTS = -1, /* predefined arguments local variable */ + ARGS_LENGTH = -2, /* number of actual args, arity if inactive */ + ARGS_CALLEE = -3, /* reference to active function's object */ + ARGS_CALLER = -4, /* arguments or call object that invoked us */ + FUN_ARITY = -5, /* number of formal parameters; desired argc */ + FUN_NAME = -6, /* function name, "" if anonymous */ + FUN_CALL = -7 /* __call__ var, function's top Call object */ +}; + +#undef TEST_BIT +#undef SET_BIT +#define TEST_BIT(tinyid, bitset) ((bitset) & PR_BIT(-1 - (tinyid))) +#define SET_BIT(tinyid, bitset) ((bitset) |= PR_BIT(-1 - (tinyid))) + +#if JS_HAS_ARGS_OBJECT + +JSObject * +js_GetArgsObject(JSContext *cx, JSStackFrame *fp) +{ + JSObject *argsobj; + + /* Create an arguments object for fp only if it lacks one. */ + PR_ASSERT(fp->fun); + argsobj = fp->argsobj; + if (argsobj) + return argsobj; + + /* Link the new object to fp so it can get actual argument values. */ + argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); + if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + fp->argsobj = argsobj; + return argsobj; +} + +static JSBool +args_enumerate(JSContext *cx, JSObject *obj); + +JSBool +js_PutArgsObject(JSContext *cx, JSStackFrame *fp) +{ + JSObject *argsobj; + JSBool ok; + JSRuntime *rt; + jsval rval; + + /* + * Reuse args_enumerate here to reflect fp's actual arguments as indexed + * elements of argsobj. + */ + argsobj = fp->argsobj; + if (!argsobj) + return JS_TRUE; + ok = args_enumerate(cx, argsobj); + + /* + * Now get the prototype properties so we snapshot fp->fun, fp->down->fun, + * and fp->argc before fp goes away. + */ + rt = cx->runtime; + ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval); + ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval); + ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.callerAtom, &rval); + ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.callerAtom, &rval); + ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval); + ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval); + + /* + * Clear the private pointer to fp, which is about to go away (js_Invoke). + * Do this last because the args_enumerate and js_GetProperty calls above + * need to follow the private slot to find fp. + */ + ok &= JS_SetPrivate(cx, argsobj, NULL); + fp->argsobj = NULL; + return ok; +} + +static JSBool +Arguments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (!cx->fp->constructing) { + obj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + return JS_TRUE; +} + +static JSPropertySpec args_props[] = { + {js_length_str, ARGS_LENGTH, 0}, + {js_callee_str, ARGS_CALLEE, 0}, + {js_caller_str, ARGS_CALLER, 0}, + {0} +}; + +static JSBool +args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsint slot; + JSStackFrame *fp; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + slot = JSVAL_TO_INT(id); + fp = JS_GetPrivate(cx, obj); + + switch (slot) { + case ARGS_CALLEE: + if (fp && !TEST_BIT(slot, fp->overrides)) + *vp = OBJECT_TO_JSVAL(fp->fun->object); + break; + + case ARGS_CALLER: + if (fp && !TEST_BIT(slot, fp->overrides)) { + if (fp->down && fp->down->fun) { + JSObject *argsobj = js_GetArgsObject(cx, fp->down); + if (!argsobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(argsobj); + } else { + *vp = JSVAL_NULL; + } + } + break; + + case ARGS_LENGTH: + if (fp && !TEST_BIT(slot, fp->overrides)) + *vp = INT_TO_JSVAL((jsint)fp->argc); + break; + + default: + if (fp && (uintN)slot < fp->argc) + *vp = fp->argv[slot]; + break; + } + return JS_TRUE; +} + +static JSBool +args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsint slot; + JSStackFrame *fp; + jsdouble argc; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + slot = JSVAL_TO_INT(id); + fp = JS_GetPrivate(cx, obj); + + switch (slot) { + case ARGS_CALLEE: + case ARGS_CALLER: + if (fp) + SET_BIT(slot, fp->overrides); + break; + + case ARGS_LENGTH: + if (fp) { + if (!js_ValueToNumber(cx, *vp, &argc)) + return JS_FALSE; + argc = js_DoubleToInteger(argc); + if (0 <= argc && argc < fp->argc) + fp->argc = (uintN)argc; + SET_BIT(slot, fp->overrides); + } + break; + + default: + if (fp && (uintN)slot < fp->argc) + fp->argv[slot] = *vp; + break; + } + return JS_TRUE; +} + +static JSBool +args_enumerate(JSContext *cx, JSObject *obj) +{ + JSStackFrame *fp; + uintN attrs, slot; + + fp = JS_GetPrivate(cx, obj); + if (!fp) + return JS_TRUE; + + /* XXX ECMA specs DontEnum, contrary to all other array-like objects */ + attrs = JSVERSION_IS_ECMA(cx->version) ? 0 : JSPROP_ENUMERATE; + + for (slot = 0; slot < fp->argc; slot++) { + if (!js_DefineProperty(cx, obj, (jsid) INT_TO_JSVAL((jsint)slot), + fp->argv[slot], NULL, NULL, attrs, + NULL)) { + return JS_FALSE; + } + } + return JS_TRUE; +} + +JSClass js_ArgumentsClass = { + "Arguments", + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, + args_getProperty, args_setProperty, + args_enumerate, JS_ResolveStub, + JS_ConvertStub, JS_FinalizeStub +}; + +#endif /* JS_HAS_ARGS_OBJECT */ #if JS_HAS_CALL_OBJECT JSObject * -js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) +js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent, + JSObject *withobj) { - JSObject *callobj, *funobj; + JSObject *callobj, *funobj, *obj; + /* Create a call object for fp only if it lacks one. */ PR_ASSERT(fp->fun); - callobj = fp->object; + callobj = fp->callobj; if (callobj) return callobj; + + /* The default call parent is its function's parent (static link). */ funobj = fp->fun->object; - if (!parent) - parent = OBJ_GET_PARENT(funobj); + if (!parent && funobj) + parent = OBJ_GET_PARENT(cx, funobj); callobj = js_NewObject(cx, &js_CallClass, NULL, parent); if (!callobj || !JS_SetPrivate(cx, callobj, fp)) { cx->newborn[GCX_OBJECT] = NULL; return NULL; } - fp->object = callobj; - if (fp->scopeChain == funobj) + fp->callobj = callobj; + + /* Splice callobj into the scope chain. */ + if (!withobj) { + for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { + if (OBJ_GET_CLASS(cx, obj) != &js_WithClass) + break; + withobj = obj; + } + } + if (withobj) + OBJ_SET_PARENT(cx, withobj, callobj); + else fp->scopeChain = callobj; return callobj; } @@ -78,147 +302,97 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) { JSObject *callobj; JSBool ok; - jsval junk; + jsid argsid; + jsval aval; /* * Reuse call_enumerate here to reflect all actual args and vars into the * call object from fp. */ - callobj = fp->object; + callobj = fp->callobj; + if (!callobj) + return JS_TRUE; ok = call_enumerate(cx, callobj); /* - * Now get the prototype properties so we snapshot fp->fun, fp->down, and - * fp->argc before fp goes away. But the predefined call.length will not - * override a formal arg or var named length -- the compiler redefines the - * name, getter, and setter. + * Get the arguments object to snapshot fp's actual argument values. */ - ok &= JS_GetProperty(cx, callobj, js_callee_str, &junk); - ok &= JS_GetProperty(cx, callobj, js_caller_str, &junk); - ok &= JS_GetProperty(cx, callobj, js_length_str, &junk); + argsid = (jsid) cx->runtime->atomState.argumentsAtom; + ok &= js_GetProperty(cx, callobj, argsid, &aval); + ok &= js_SetProperty(cx, callobj, argsid, &aval); + ok &= js_PutArgsObject(cx, fp); /* - * Clear the private pointer to fp, which is about to go away (in Call). - * Do this last because the js_GetProperty calls above need to follow the - * private slot to find fp, in order to copy back args and locals. + * Clear the private pointer to fp, which is about to go away (js_Invoke). + * Do this last because the call_enumerate and js_GetProperty calls above + * need to follow the private slot to find fp. */ ok &= JS_SetPrivate(cx, callobj, NULL); - return ok; -} - -JSBool -js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool ok; - JSStackFrame *fp; - JSSymbol *sym; - - ok = JS_TRUE; - fp = JS_GetPrivate(cx, obj); - if (fp && fp->script) { - JS_LOCK(cx); - for (sym = fp->script->vars; sym; sym = sym->next) { - if (sym_property(sym)->id == id) { - if (!js_GetProperty(cx, obj, sym_id(sym), vp)) - ok = JS_FALSE; - break; - } - } - JS_UNLOCK(cx); - } - return ok; -} - -JSBool -js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool ok; - JSStackFrame *fp; - JSSymbol *sym; - - ok = JS_TRUE; - fp = JS_GetPrivate(cx, obj); - if (fp && fp->script) { - JS_LOCK(cx); - for (sym = fp->script->vars; sym; sym = sym->next) { - if (sym_property(sym)->id == id) { - if (!js_SetProperty(cx, obj, sym_id(sym), vp)) - ok = JS_FALSE; - break; - } - } - JS_UNLOCK(cx); - } + fp->callobj = NULL; return ok; } static JSBool Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + if (!cx->fp->constructing) { + obj = js_NewObject(cx, &js_CallClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } return JS_TRUE; } -enum call_tinyid { - CALL_ARGUMENTS = -1, - CALL_CALLEE = -2, - CALL_CALLER = -3, - CALL_LENGTH = -4 -}; - static JSPropertySpec call_props[] = { - {js_arguments_str, CALL_ARGUMENTS, JSPROP_READONLY}, - {js_callee_str, CALL_CALLEE, JSPROP_READONLY}, - {js_caller_str, CALL_CALLER, JSPROP_READONLY}, - {js_length_str, CALL_LENGTH, JSPROP_READONLY}, + {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT}, + {"__callee__", ARGS_CALLEE, 0}, + {"__caller__", ARGS_CALLER, 0}, + {"__call__", FUN_CALL, 0}, {0} }; -#define IS_CALL_TINYID(slot) (CALL_LENGTH <= slot && slot <= CALL_ARGUMENTS) -#define CALL_TINYID_NAME(slot) call_props[-(slot)-1].name - static JSBool call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; - JSObject *callobj; jsint slot; + fp = JS_GetPrivate(cx, obj); if (!JSVAL_IS_INT(id)) return JS_TRUE; - - fp = JS_GetPrivate(cx, obj); slot = JSVAL_TO_INT(id); - /* XXX don't pollute call scope with unqualified call property names */ - if (fp && - fp->pc && - (js_CodeSpec[*fp->pc].format & JOF_MODEMASK) == JOF_NAME && - IS_CALL_TINYID(slot)) { - return JS_GetProperty(cx, fp->fun->object, CALL_TINYID_NAME(slot), vp); - } - switch (slot) { case CALL_ARGUMENTS: - *vp = OBJECT_TO_JSVAL(obj); - break; - - case CALL_CALLEE: - if (fp) - *vp = OBJECT_TO_JSVAL(fp->fun->object); - break; - - case CALL_CALLER: - if (fp && fp->down && fp->down->fun) { - callobj = js_GetCallObject(cx, fp->down, NULL); - if (!callobj) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(callobj); + if (fp && !TEST_BIT(slot, fp->overrides)) { + JSObject *argsobj = js_GetArgsObject(cx, fp); + if (!argsobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(argsobj); } break; - case CALL_LENGTH: - if (fp) - *vp = INT_TO_JSVAL((jsint)fp->argc); + case ARGS_CALLEE: + if (fp && !TEST_BIT(slot, fp->overrides)) + *vp = OBJECT_TO_JSVAL(fp->fun->object); + break; + + case ARGS_CALLER: + if (fp && !TEST_BIT(slot, fp->overrides)) { + if (fp->down && fp->down->fun) { + JSObject *callobj = js_GetCallObject(cx, fp->down, NULL, NULL); + if (!callobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(callobj); + } else { + *vp = JSVAL_NULL; + } + } + break; + + case FUN_CALL: + *vp = OBJECT_TO_JSVAL(obj); break; default: @@ -235,14 +409,24 @@ call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSStackFrame *fp; jsint slot; + fp = JS_GetPrivate(cx, obj); if (!JSVAL_IS_INT(id)) return JS_TRUE; + slot = JSVAL_TO_INT(id); - fp = JS_GetPrivate(cx, obj); - if (fp) { - slot = JSVAL_TO_INT(id); - if ((uintN)slot < fp->argc) + switch (slot) { + case CALL_ARGUMENTS: + case ARGS_CALLEE: + case ARGS_CALLER: + case FUN_CALL: + if (fp) + SET_BIT(slot, fp->overrides); + break; + + default: + if (fp && (uintN)slot < fp->argc) fp->argv[slot] = *vp; + break; } return JS_TRUE; } @@ -278,113 +462,52 @@ call_setVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_TRUE; } -static JSBool -call_reflectArgument(JSContext *cx, JSObject *obj, JSStackFrame *fp, - JSSymbol *sym) -{ - uintN slot; - JSProperty *prop; - JSScope *scope; - JSBool ok; - - JS_LOCK(cx); - slot = (uintN)JSVAL_TO_INT(sym_property(sym)->id); - prop = js_DefineProperty(cx, obj, (jsval)sym_atom(sym), - (slot < fp->argc) ? fp->argv[slot] : JSVAL_VOID, - NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT); - if (!prop) { - ok = JS_FALSE; - } else { - prop->id = INT_TO_JSVAL(slot); - scope = (JSScope *)obj->map; - ok = (scope->ops->add(cx, scope, INT_TO_JSVAL(slot), prop) != NULL); - } - JS_UNLOCK(cx); - return ok; -} - -static JSBool -call_reflectVariable(JSContext *cx, JSObject *obj, JSStackFrame *fp, - JSSymbol *sym) -{ - uintN slot; - JSProperty *prop; - JSBool ok; - - slot = (uintN)JSVAL_TO_INT(sym_property(sym)->id); - JS_LOCK(cx); - prop = js_DefineProperty(cx, obj, (jsval)sym_atom(sym), - (slot < fp->nvars) ? fp->vars[slot] : JSVAL_VOID, - call_getVariable, call_setVariable, - JSPROP_PERMANENT); - ok = (prop != NULL); - if (ok) - prop->id = INT_TO_JSVAL(slot); - JS_UNLOCK(cx); - return ok; -} - static JSBool call_enumerate(JSContext *cx, JSObject *obj) { JSStackFrame *fp; JSFunction *fun; - JSSymbol *sym; - JSBool ok; + JSScope *scope; + JSScopeProperty *sprop; + JSPropertyOp getter; JSProperty *prop; - uintN slot; fp = JS_GetPrivate(cx, obj); if (!fp) return JS_TRUE; fun = fp->fun; - if (!fun->script) + if (!fun->script || !fun->object) return JS_TRUE; - ok = JS_TRUE; - JS_LOCK(cx); + /* Reflect actual args for formal parameters, and all local variables. */ + scope = (JSScope *)fun->object->map; + for (sprop = scope->props; sprop; sprop = sprop->next) { + getter = sprop->getter; + if (getter != js_GetArgument && getter != js_GetLocalVariable) + continue; - /* First, reflect actuals that match formal arguments. */ - if (fun->script) { - for (sym = fun->script->args; sym; sym = sym->next) { - ok = js_LookupProperty(cx, obj, sym_id(sym), &obj, &prop); - if (!ok) - goto out; - } + /* Trigger reflection in call_resolve by doing a lookup. */ + if (!js_LookupProperty(cx, obj, sym_id(sprop->symbols), &obj, &prop)) + return JS_FALSE; + OBJ_DROP_PROPERTY(cx, obj, prop); } - /* Next, copy any actual arguments that lacked formals. */ - for (slot = fun->nargs; slot < fp->argc; slot++) { - if (!js_SetProperty(cx, obj, INT_TO_JSVAL((jsint)slot), - &fp->argv[slot])) { - ok = JS_FALSE; - goto out; - } - } - - /* Finally, reflect variables. */ - if (fun->script) { - for (sym = fun->script->vars; sym; sym = sym->next) { - ok = js_LookupProperty(cx, obj, sym_id(sym), &obj, &prop); - if (!ok) - goto out; - } - } - -out: - JS_UNLOCK(cx); - return ok; + return JS_TRUE; } static JSBool -call_resolve(JSContext *cx, JSObject *obj, jsval id, JSObject **objp) +call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) { JSStackFrame *fp; JSString *str; JSAtom *atom; - JSBool ok; - JSProperty *prop; - JSSymbol *sym; + JSObject *obj2; + JSScopeProperty *sprop; + JSPropertyOp getter, setter; + jsval propid, *vp; + jsid symid; + uintN slot, nslots; fp = JS_GetPrivate(cx, obj); if (!fp) @@ -393,28 +516,47 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, JSObject **objp) if (!JSVAL_IS_STRING(id)) return JS_TRUE; - str = JSVAL_TO_STRING(id); - JS_LOCK(cx); - atom = js_AtomizeString(cx, str, 0); - if (!atom) { - ok = JS_FALSE; - } else { - ok = js_LookupProperty(cx, fp->fun->object, (jsval)atom, &obj, &prop); - js_DropAtom(cx, atom); - } - JS_UNLOCK(cx); - if (!ok) - return JS_FALSE; + if (!fp->fun->object) + return JS_TRUE; - if (prop) { - sym = prop->symbols; - if (prop->getter == js_GetArgument) { - if (!call_reflectArgument(cx, obj, fp, sym)) - return JS_FALSE; - *objp = obj; - } else if (prop->getter == js_GetLocalVariable) { - if (!call_reflectVariable(cx, obj, fp, sym)) - return JS_FALSE; + str = JSVAL_TO_STRING(id); + atom = js_AtomizeString(cx, str, 0); + if (!atom) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, fp->fun->object, (jsid)atom, &obj2, + (JSProperty **)&sprop)) { + return JS_FALSE; + } + + if (sprop) { + getter = sprop->getter; + propid = sprop->id; + symid = (jsid) sym_atom(sprop->symbols); + OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop); + vp = NULL; + if (getter == js_GetArgument) { + vp = fp->argv; + nslots = fp->argc; + getter = setter = NULL; + } else if (getter == js_GetLocalVariable) { + vp = fp->vars; + nslots = fp->nvars; + getter = call_getVariable; + setter = call_setVariable; + } + if (vp) { + slot = (uintN)JSVAL_TO_INT(propid); + if (!js_DefineProperty(cx, obj, symid, + (slot < nslots) ? vp[slot] : JSVAL_VOID, + getter, setter, + JSPROP_ENUMERATE | JSPROP_PERMANENT, + (JSProperty **)&sprop)) { + return JS_FALSE; + } + PR_ASSERT(sprop); + if (slot < nslots) + sprop->id = INT_TO_JSVAL(slot); + OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop); *objp = obj; } } @@ -443,41 +585,7 @@ JSClass js_CallClass = { call_convert, JS_FinalizeStub }; -/* Forward declaration. */ -static JSBool -fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static JSBool -call_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSStackFrame *fp; - - if (!JS_InstanceOf(cx, obj, &js_CallClass, argv)) - return JS_FALSE; - fp = JS_GetPrivate(cx, obj); - if (fp) - return fun_toString(cx, fp->fun->object, argc, argv, rval); - return js_obj_toString(cx, obj, argc, argv, rval); -} - -static JSBool -call_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - if (!JS_InstanceOf(cx, obj, &js_CallClass, argv)) - return JS_FALSE; - return JS_GetProperty(cx, obj, js_callee_str, rval); -} - -static JSFunctionSpec call_methods[] = { - {js_toString_str, call_toString, 1}, - {js_valueOf_str, call_valueOf, 0}, - {0} -}; - -#endif /* !JS_HAS_CALL_OBJECT */ +#endif /* JS_HAS_CALL_OBJECT */ #if JS_HAS_LEXICAL_CLOSURE static JSBool @@ -487,36 +595,38 @@ Closure(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSObject *varParent, *closureParent; JSFunction *fun; - if (!JS_InstanceOf(cx, obj, &js_ClosureClass, argv)) - return JS_FALSE; + if (!cx->fp->constructing) { + obj = js_NewObject(cx, &js_ClosureClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } if (!(caller = cx->fp->down) || !caller->scopeChain) return JS_TRUE; - JS_LOCK_VOID(cx, varParent = js_FindVariableScope(cx, &fun)); + varParent = js_FindVariableScope(cx, &fun); if (!varParent) return JS_FALSE; closureParent = caller->scopeChain; if (argc != 0) { - fun = JS_ValueToFunction(cx, argv[0]); + fun = js_ValueToFunction(cx, &argv[0], JS_FALSE); if (!fun) return JS_FALSE; - OBJ_SET_PROTO(obj, fun->object); + OBJ_SET_PROTO(cx, obj, fun->object); if (argc > 1) { - if (!JS_ValueToObject(cx, argv[1], &closureParent)) + if (!js_ValueToObject(cx, argv[1], &closureParent)) return JS_FALSE; } } - OBJ_SET_PARENT(obj, closureParent); + OBJ_SET_PARENT(cx, obj, closureParent); /* Make sure constructor is not inherited from fun->object. */ - if (!js_DefineProperty(cx, obj, - (jsval)cx->runtime->atomState.constructorAtom, - argv[-2], NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - return JS_FALSE; - } - return JS_TRUE; + return js_DefineProperty(cx, obj, + (jsid)cx->runtime->atomState.constructorAtom, + argv[-2], NULL, NULL, + JSPROP_READONLY | JSPROP_PERMANENT, + NULL); } static JSBool @@ -525,104 +635,165 @@ closure_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) JSObject *proto; if (type == JSTYPE_FUNCTION) { - proto = JS_GetPrototype(cx, obj); - if (proto) { - PR_ASSERT(proto->map->clasp == &js_FunctionClass); + proto = OBJ_GET_PROTO(cx, obj); + if (proto) *vp = OBJECT_TO_JSVAL(proto); - } } return JS_TRUE; } +static JSBool +closure_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSStackFrame *fp; + JSObject *closure, *callobj; + JSFunction *fun; + jsval junk; + + /* Get a call object to link the closure's parent into the scope chain. */ + fp = cx->fp; + closure = JSVAL_TO_OBJECT(argv[-2]); + PR_ASSERT(OBJ_GET_CLASS(cx, closure) == &js_ClosureClass); + callobj = js_GetCallObject(cx, fp, OBJ_GET_PARENT(cx, closure), NULL); + if (!callobj) + return JS_FALSE; + fp->scopeChain = callobj; + + /* Make the function object, not its closure, available as argv[-2]. */ + fun = fp->fun; + argv[-2] = OBJECT_TO_JSVAL(fun->object); + if (fun->call) + return fun->call(cx, obj, argc, argv, rval); + if (fun->script) + return js_Interpret(cx, &junk); + return JS_TRUE; +} + JSClass js_ClosureClass = { "Closure", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, closure_convert, JS_FinalizeStub + JS_EnumerateStub, JS_ResolveStub, closure_convert, JS_FinalizeStub, + NULL, NULL, closure_call, closure_call }; #endif /* JS_HAS_LEXICAL_CLOSURE */ -enum fun_tinyid { - FUN_ARGUMENTS = -1, /* actual arguments for this call to fun */ - FUN_CALLER = -2, /* bogus, the function that called fun */ - FUN_ARITY = -3 /* number of formals if inactive, else argc */ -}; - static JSPropertySpec function_props[] = { - {js_arguments_str, FUN_ARGUMENTS, JSPROP_READONLY | JSPROP_PERMANENT}, - {js_caller_str, FUN_CALLER, JSPROP_READONLY | JSPROP_PERMANENT}, - {js_arity_str, FUN_ARITY, JSPROP_READONLY | JSPROP_PERMANENT}, + /* + * We make fun.arguments readonly in fun_setProperty, unless it is being + * set by an unqualified assignment 'arguments = ...' within a call where + * fun->object is proxying for a Call object. + */ + {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT}, + {"__arity__", FUN_ARITY, JSPROP_READONLY | JSPROP_PERMANENT}, + {"__length__", ARGS_LENGTH, JSPROP_READONLY | JSPROP_PERMANENT}, + {"__caller__", ARGS_CALLER, JSPROP_READONLY | JSPROP_PERMANENT}, + {"__name__", FUN_NAME, JSPROP_READONLY | JSPROP_PERMANENT}, + {"__call__", FUN_CALL, JSPROP_READONLY | JSPROP_PERMANENT}, {0} }; +static JSBool +fun_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + /* Make delete f.length fail even though length is in f.__proto__. */ + 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)) { + *vp = JSVAL_FALSE; + } + } + return JS_TRUE; +} + static JSBool fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSFunction *fun; - JSStackFrame *fp, *bestfp; + JSStackFrame *fp; jsint slot; #if defined XP_PC && defined _MSC_VER &&_MSC_VER <= 800 /* MSVC1.5 coredumps */ jsval bogus = *vp; #endif - if (!JSVAL_IS_INT(id)) - return JS_TRUE; + if (!JSVAL_IS_INT(id)) { + /* Map qualified access of the form f.arity to f.__arity__. */ + if (id == ATOM_KEY(cx->runtime->atomState.arityAtom)) { + id = INT_TO_JSVAL(FUN_ARITY); + } else if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { + id = INT_TO_JSVAL(ARGS_LENGTH); + } else if (id == ATOM_KEY(cx->runtime->atomState.callerAtom)) { + id = INT_TO_JSVAL(ARGS_CALLER); + } else if (id == ATOM_KEY(cx->runtime->atomState.nameAtom)) { + id = INT_TO_JSVAL(FUN_NAME); + } else { + return JS_TRUE; + } + } + /* No valid function object should lack private data, but check anyway. */ fun = JS_GetPrivate(cx, obj); if (!fun) return JS_TRUE; - /* Find fun's top-most frame, or the top-most non-native frame. */ - bestfp = NULL; - for (fp = cx->fp; fp && fp->fun && fp->fun != fun; fp = fp->down) { - if (fp->fun->call) - continue; - if (!bestfp) - bestfp = fp; - } - - /* Use bestfp only if this property reference is unqualified. */ - if ((!fp || !fp->fun) && - bestfp && - bestfp->pc && - (js_CodeSpec[*bestfp->pc].format & JOF_MODEMASK) == JOF_NAME) { - fp = bestfp; - } + /* Find fun's top-most activation record. */ + for (fp = cx->fp; fp && (fp->fun != fun || fp->debugging); fp = fp->down) + continue; slot = (jsint)JSVAL_TO_INT(id); switch (slot) { -#if JS_HAS_CALL_OBJECT - case FUN_ARGUMENTS: +#if JS_HAS_ARGS_OBJECT + case CALL_ARGUMENTS: if (fp && fp->fun) { - JSObject *callobj = js_GetCallObject(cx, fp, NULL); - if (!callobj) + JSObject *argsobj = js_GetArgsObject(cx, fp); + if (!argsobj) return JS_FALSE; - *vp = OBJECT_TO_JSVAL(callobj); + *vp = OBJECT_TO_JSVAL(argsobj); } else { *vp = JSVAL_NULL; } break; -#else /* !JS_HAS_CALL_OBJECT */ - case FUN_ARGUMENTS: +#else /* !JS_HAS_ARGS_OBJECT */ + case CALL_ARGUMENTS: *vp = OBJECT_TO_JSVAL(fp ? obj : NULL); break; -#endif /* !JS_HAS_CALL_OBJECT */ +#endif /* !JS_HAS_ARGS_OBJECT */ - case FUN_CALLER: + case ARGS_LENGTH: + if (!JSVERSION_IS_ECMA(cx->version)) + *vp = INT_TO_JSVAL((jsint)(fp->fun ? fp->argc : fun->nargs)); + else + case FUN_ARITY: + *vp = INT_TO_JSVAL((jsint)fun->nargs); + break; + + case ARGS_CALLER: if (fp && fp->down && fp->down->fun) *vp = OBJECT_TO_JSVAL(fp->down->fun->object); else *vp = JSVAL_NULL; break; - case FUN_ARITY: - if (cx->version < JSVERSION_1_2) - *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs)); - else - *vp = INT_TO_JSVAL((jsint)fun->nargs); + case FUN_NAME: + *vp = fun->atom + ? ATOM_KEY(fun->atom) + : STRING_TO_JSVAL(cx->runtime->emptyString); break; + case FUN_CALL: + if (fp && fp->fun) { + JSObject *callobj = js_GetCallObject(cx, fp, NULL, NULL); + if (!callobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(callobj); + } else { + *vp = JSVAL_NULL; + } + break; + default: /* XXX fun[0] and fun.arguments[0] are equivalent. */ if (fp && fp->fun && (uintN)slot < fp->argc) @@ -638,31 +809,119 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) } static JSBool -fun_resolve(JSContext *cx, JSObject *obj, jsval id, JSObject **objp) +fun_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSFunction *fun; - JSString *str; + JSStackFrame *fp; + JSObject *callobj; - if (!JSVAL_IS_STRING(id)) + /* Handle only the setting of arguments or fun.arguments in active fun. */ + if (!JSVAL_IS_INT(id) || JSVAL_TO_INT(id) != CALL_ARGUMENTS) return JS_TRUE; + /* No valid function object should lack private data, but check anyway. */ fun = JS_GetPrivate(cx, obj); if (!fun) return JS_TRUE; - str = JSVAL_TO_STRING(id); - if (str == ATOM_TO_STRING(cx->runtime->atomState.classPrototypeAtom)) { - JSObject *proto; + /* Find the top-most non-native activation record, which must be fun's. */ + for (fp = cx->fp; ; fp = fp->down) { + if (!fp) + goto _readonly; + if (fp->fun == fun) { + if (!fp->debugging) + break; + } else { + if (fp->script) + goto _readonly; + } + } - proto = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + /* Set only if unqualified: 'arguments = ...' not 'fun.arguments = ...'. */ + if (!fp->pc || (js_CodeSpec[*fp->pc].format & JOF_MODEMASK) != JOF_NAME) + goto _readonly; + + /* Get a Call object for fp and set its arguments property to vp. */ + callobj = js_GetCallObject(cx, fp, NULL, NULL); + if (!callobj) + return JS_FALSE; + return js_SetProperty(cx, callobj, + (jsid)cx->runtime->atomState.argumentsAtom, + vp); + +/* "readonly" can be a language extension on OSF */ +_readonly: + if (JSVERSION_IS_ECMA(cx->version)) + return fun_getProperty(cx, obj, id, vp); + JS_ReportError(cx, "%s is read-only", js_arguments_str); + return JS_FALSE; +} + +static JSBool +fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + JSFunction *fun; + JSString *str; + JSAtom *prototypeAtom; + + if (!JSVAL_IS_STRING(id)) + return JS_TRUE; + + /* No valid function object should lack private data, but check anyway. */ + fun = JS_GetPrivate(cx, obj); + if (!fun || !fun->object) + return JS_TRUE; + + /* No need to reflect fun.prototype in 'fun.prototype = ...'. */ + flags &= JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING; + if (flags == (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) + return JS_TRUE; + + /* Hide prototype if fun->object is proxying for a Call object. */ + if (!(flags & JSRESOLVE_QUALIFIED)) { + if (cx->fp && cx->fp->fun == fun && !cx->fp->callobj) + return JS_TRUE; + } + + str = JSVAL_TO_STRING(id); + prototypeAtom = cx->runtime->atomState.classPrototypeAtom; + if (str == ATOM_TO_STRING(prototypeAtom)) { + JSObject *proto; + jsval pval; + + proto = NULL; + if (fun->object != obj) { + /* + * Clone of a function: make its prototype property value have the + * same class as the clone-parent's prototype. + */ + if (!OBJ_GET_PROPERTY(cx, fun->object, (jsid)prototypeAtom, &pval)) + return JS_FALSE; + if (JSVAL_IS_OBJECT(pval)) + proto = JSVAL_TO_OBJECT(pval); + } + + /* If resolving "prototype" in a clone, clone the parent's prototype. */ + if (proto) + proto = js_NewObject(cx, OBJ_GET_CLASS(cx, proto), proto, NULL); + else + proto = js_NewObject(cx, &js_ObjectClass, NULL, NULL); if (!proto) return JS_FALSE; - if (!js_SetClassPrototype(cx, fun, proto)) { + + /* + * ECMA says that constructor.prototype is DontEnum for user-defined + * functions, but DontEnum | ReadOnly | DontDelete for native "system" + * constructors such as Object or Function. So lazily set the former + * here in fun_resolve, but eagerly define the latter in JS_InitClass, + * with the right attributes. + */ + if (!js_SetClassPrototype(cx, fun->object, proto, 0)) { cx->newborn[GCX_OBJECT] = NULL; return JS_FALSE; } *objp = obj; - return JS_TRUE; } return JS_TRUE; @@ -672,7 +931,6 @@ static JSBool fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { switch (type) { - case JSTYPE_OBJECT: case JSTYPE_FUNCTION: *vp = OBJECT_TO_JSVAL(obj); break; @@ -686,167 +944,323 @@ fun_finalize(JSContext *cx, JSObject *obj) { JSFunction *fun; + /* No valid function object should lack private data, but check anyway. */ fun = JS_GetPrivate(cx, obj); if (!fun) return; - if (fun->atom) - JS_LOCK_VOID(cx, js_DropAtom(cx, fun->atom)); + if (fun->object == obj) + fun->object = NULL; + JS_ATOMIC_ADDREF(&fun->nrefs, -1); + if (fun->nrefs) + return; if (fun->script) js_DestroyScript(cx, fun->script); JS_free(cx, fun); } +#if JS_HAS_XDR + +#include "jsxdrapi.h" + +enum { + JSXDR_FUNARG = 1, + JSXDR_FUNVAR = 2 +}; + +/* XXX store parent and proto, if defined */ +static JSBool +fun_xdrObject(JSXDRState *xdr, JSObject **objp) +{ + JSFunction *fun; + JSString *atomstr; + char *propname; + JSScopeProperty *sprop; + JSBool magic; + uintN i; + uint32 type; +#ifdef DEBUG + uintN nvars = 0, nargs = 0; +#endif + + if (xdr->mode == JSXDR_ENCODE) { + /* + * No valid function object should lack private data, but fail soft + * (return true, no error report) in case one does due to API pilot + * or internal error. + */ + fun = JS_GetPrivate(xdr->cx, *objp); + if (!fun) + return JS_TRUE; + atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; + } else { + fun = js_NewFunction(xdr->cx, NULL, NULL, 0, 0, NULL, NULL); + if (!fun) + return JS_FALSE; + } + + if (!JS_XDRStringOrNull(xdr, &atomstr) || + !JS_XDRUint8(xdr, &fun->nargs) || + !JS_XDRUint8(xdr, &fun->flags) || + !JS_XDRUint16(xdr, &fun->extra) || + !JS_XDRUint16(xdr, &fun->nvars)) + return JS_FALSE; + + /* do arguments and local vars */ + if (fun->object) { + if (xdr->mode == JSXDR_ENCODE) { + JSScope *scope = (JSScope *) fun->object->map; + + for (sprop = scope->props; sprop; sprop = sprop->next) { + if (sprop->getter == js_GetArgument) { + type = JSXDR_FUNARG; + PR_ASSERT(nargs++ <= fun->nargs); + } else if (sprop->getter == js_GetLocalVariable) { + type = JSXDR_FUNVAR; + PR_ASSERT(nvars++ <= fun->nvars); + } else { + continue; + } + propname = ATOM_BYTES(sym_atom(sprop->symbols)); + if (!JS_XDRUint32(xdr, &type) || + !JS_XDRCString(xdr, &propname)) + return JS_FALSE; + } + } else { + JSPropertyOp getter, setter; + + i = fun->nvars + fun->nargs; + while (i--) { + if (!JS_XDRUint32(xdr, &type) || + !JS_XDRCString(xdr, &propname)) + return JS_FALSE; + PR_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR); + if (type == JSXDR_FUNARG) { + getter = js_GetArgument; + setter = js_SetArgument; + PR_ASSERT(nargs++ <= fun->nargs); + } else if (type == JSXDR_FUNVAR) { + getter = js_GetLocalVariable; + setter = js_SetLocalVariable; + PR_ASSERT(nvars++ <= fun->nvars); + } + if (!JS_DefineProperty(xdr->cx, fun->object, propname, + JSVAL_VOID, getter, setter, + JSPROP_ENUMERATE | JSPROP_PERMANENT)) { + JS_free(xdr->cx, propname); + return JS_FALSE; + } + JS_free(xdr->cx, propname); + } + } + } + if (!js_XDRScript(xdr, &fun->script, &magic) || + !magic) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + if (atomstr) { + fun->atom = js_AtomizeString(xdr->cx, atomstr, 0); + if (!fun->atom) + return JS_FALSE; + } + *objp = fun->object; + } + + return JS_TRUE; +} + +#else /* !JS_HAS_XDR */ + +#define fun_xdrObject NULL + +#endif /* !JS_HAS_XDR */ + +#if JS_HAS_INSTANCEOF + +static JSBool +fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + jsval pval; + + if (!OBJ_GET_PROPERTY(cx, obj, + (jsid)cx->runtime->atomState.classPrototypeAtom, + &pval)) { + return JS_FALSE; + } + if (JSVAL_IS_PRIMITIVE(pval)) { + *bp = JS_FALSE; + return JS_TRUE; + } + return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); +} + +#else /* !JS_HAS_INSTANCEOF */ + +#define fun_hasInstance NULL + +#endif /* !JS_HAS_INSTANCEOF */ + JSClass js_FunctionClass = { "Function", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, - JS_PropertyStub, JS_PropertyStub, - fun_getProperty, JS_PropertyStub, + JS_PropertyStub, fun_delProperty, + fun_getProperty, fun_setProperty, JS_EnumerateStub, (JSResolveOp)fun_resolve, - fun_convert, fun_finalize + fun_convert, fun_finalize, + NULL, NULL, + NULL, NULL, + fun_xdrObject, fun_hasInstance }; static JSBool fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + jsval fval; JSFunction *fun; - jsdouble d; + uint32 indent; JSString *str; - fun = JS_GetInstancePrivate(cx, obj, &js_FunctionClass, argv); + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) + return JS_FALSE; + fval = argv[-1]; + if (!JSVAL_IS_FUNCTION(cx, fval)) + return js_obj_toString(cx, obj, argc, argv, rval); + obj = JSVAL_TO_OBJECT(fval); + fun = JS_GetPrivate(cx, obj); if (!fun) + return JS_TRUE; + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) return JS_FALSE; - d = 0; - if (argc && !JS_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - str = JS_DecompileFunction(cx, fun, (uintN)d); + str = JS_DecompileFunction(cx, fun, (uintN)indent); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } -#if JS_HAS_APPLY_FUNCTION +#if JS_HAS_CALL_FUNCTION static JSBool -fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - jsval fval, *nargv; - uintN i, nargc, nargvlen; - JSObject *aobj; - jsint index, length; + jsval fval, *sp, *oldsp; + void *mark; + uintN i; + JSStackFrame *fp; JSBool ok; - if (!obj->map->clasp->convert(cx, obj, JSTYPE_FUNCTION, &fval)) + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) return JS_FALSE; + fval = argv[-1]; if (argc == 0) { /* Call fun with its parent as the 'this' parameter if no args. */ - obj = OBJ_GET_PARENT(obj); + obj = OBJ_GET_PARENT(cx, obj); } else { /* Otherwise convert the first arg to 'this' and skip over it. */ - if (!JS_ValueToObject(cx, argv[0], &obj)) + if (!js_ValueToObject(cx, argv[0], &obj)) return JS_FALSE; argc--; argv++; } - nargv = NULL; - nargc = nargvlen = 0; - for (i = 0; i < argc; i++) { - if (JSVAL_IS_OBJECT(argv[i])) { - aobj = JSVAL_TO_OBJECT(argv[i]); - if (aobj && JS_HasLengthProperty(cx, aobj)) { - ok = JS_GetArrayLength(cx, aobj, &length); - if (!ok) - goto out; - if (length == 0) - continue; - if (!nargv) - nargvlen = argc; - nargvlen += (uintN)(length - 1); - nargv = nargv - ? JS_realloc(cx, nargv, nargvlen * sizeof(jsval)) - : JS_malloc(cx, nargvlen * sizeof(jsval)); - if (!nargv) - return JS_FALSE; - for (index = 0; index < length; index++) { - ok = JS_GetElement(cx, aobj, index, &nargv[nargc]); - if (!ok) - goto out; - nargc++; - } - continue; - } - } + /* Allocate stack space for fval, obj, and the args. */ + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) + return JS_FALSE; - if (!nargv) { - nargvlen = argc; - nargv = JS_malloc(cx, nargvlen * sizeof(jsval)); - if (!nargv) - return JS_FALSE; - } - nargv[nargc] = argv[i]; - nargc++; + /* Push fval, obj, and the args. */ + *sp++ = fval; + *sp++ = OBJECT_TO_JSVAL(obj); + for (i = 0; i < argc; i++) + *sp++ = argv[i]; + + /* Lift current frame to include the args and do the call. */ + fp = cx->fp; + oldsp = fp->sp; + fp->sp = sp; + ok = js_Invoke(cx, argc, JS_FALSE); + + /* Store rval and pop stack back to our frame's sp. */ + *rval = fp->sp[-1]; + fp->sp = oldsp; + js_FreeStack(cx, mark); + return ok; +} +#endif /* JS_HAS_CALL_FUNCTION */ + +#if JS_HAS_APPLY_FUNCTION +static JSBool +fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval fval, *sp, *oldsp; + JSObject *aobj; + jsuint length; + void *mark; + uintN i; + JSBool ok; + JSStackFrame *fp; + + if (argc != 2 || + !JSVAL_IS_OBJECT(argv[1]) || + !(aobj = JSVAL_TO_OBJECT(argv[1])) || + !js_HasLengthProperty(cx, aobj, &length)) { + return fun_call(cx, obj, argc, argv, rval); } - ok = js_Call(cx, obj, fval, nargc, nargv, rval); + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) + return JS_FALSE; + fval = argv[-1]; + + /* Convert the first arg to 'this' and skip over it. */ + if (!js_ValueToObject(cx, argv[0], &obj)) + return JS_FALSE; + + /* Allocate stack space for fval, obj, and the args. */ + argc = (uintN)length; + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) + return JS_FALSE; + + /* Push fval, obj, and aobj's elements as args. */ + *sp++ = fval; + *sp++ = OBJECT_TO_JSVAL(obj); + for (i = 0; i < argc; i++) { + ok = JS_GetElement(cx, aobj, (jsint)i, sp); + if (!ok) + goto out; + sp++; + } + + /* Lift current frame to include the args and do the call. */ + fp = cx->fp; + oldsp = fp->sp; + fp->sp = sp; + ok = js_Invoke(cx, argc, JS_FALSE); + + /* Store rval and pop stack back to our frame's sp. */ + *rval = fp->sp[-1]; + fp->sp = oldsp; out: - if (nargv) - JS_free(cx, nargv); + js_FreeStack(cx, mark); return ok; } #endif /* JS_HAS_APPLY_FUNCTION */ static JSFunctionSpec function_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, fun_toString, 0}, +#endif {js_toString_str, fun_toString, 1}, #if JS_HAS_APPLY_FUNCTION {"apply", fun_apply, 1}, +#endif +#if JS_HAS_CALL_FUNCTION + {"call", fun_call, 1}, #endif {0} }; -static JSFunction * -NewFunction(JSContext *cx, JSObject *obj, JSNative call, uintN nargs, - uintN flags, JSObject *parent, JSAtom *atom) -{ - JSFunction *fun; - - /* Allocate a function struct. */ - fun = JS_malloc(cx, sizeof *fun); - if (!fun) - return NULL; - - /* If obj is null, allocate an object for it. */ - if (!obj) { - obj = js_NewObject(cx, &js_FunctionClass, NULL, parent); - if (!obj) { - JS_free(cx, fun); - return NULL; - } - } else { - OBJ_SET_PARENT(obj, parent); - } - - /* Link fun to obj and vice versa. */ - fun->object = obj; - if (!JS_SetPrivate(cx, obj, fun)) { - cx->newborn[GCX_OBJECT] = NULL; - JS_free(cx, fun); - return NULL; - } - - /* Initialize remaining function members. */ - fun->call = call; - fun->nargs = nargs; - fun->flags = flags; - fun->nvars = 0; - if (atom) - JS_LOCK_VOID(cx, fun->atom = js_HoldAtom(cx, atom)); - else - fun->atom = NULL; - fun->script = NULL; - return fun; -} - JSBool js_IsIdentifier(JSString *str) { @@ -874,18 +1288,18 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) uintN i, n, lineno; JSAtom *atom; const char *filename; - JSProperty *prop; - JSSymbol *arg, *args, **argp; + JSObject *obj2; + JSScopeProperty *sprop; JSString *str; JSStackFrame *fp; JSTokenStream *ts; - JSBool ok; JSPrincipals *principals; - if (obj->map->clasp != &js_FunctionClass) { + if (cx->fp && !cx->fp->constructing) { obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); if (!obj) return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); } fun = JS_GetPrivate(cx, obj); if (fun) @@ -902,78 +1316,72 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * its running context's globalObject, which might be different from the * top-level reachable from scopeChain (in HTML frames, e.g.). */ - parent = argv ? OBJ_GET_PARENT(JSVAL_TO_OBJECT(argv[-2])) : NULL; + parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2])); #else /* Set up for dynamic parenting (see Call in jsinterp.c). */ parent = NULL; #endif - fun = NewFunction(cx, obj, NULL, 0, 0, parent, - (cx->version >= JSVERSION_1_2) - ? NULL - : cx->runtime->atomState.anonymousAtom); + fun = js_NewFunction(cx, obj, NULL, 0, 0, parent, + (cx->version >= JSVERSION_1_2) + ? NULL + : cx->runtime->atomState.anonymousAtom); if (!fun) return JS_FALSE; - args = NULL; - argp = &args; n = argc ? argc - 1 : 0; - JS_LOCK(cx); for (i = 0; i < n; i++) { atom = js_ValueToStringAtom(cx, argv[i]); - if (!atom) { - ok = JS_FALSE; - goto out; - } + if (!atom) + return JS_FALSE; str = ATOM_TO_STRING(atom); if (!js_IsIdentifier(str)) { JS_ReportError(cx, "illegal formal argument name %s", JS_GetStringBytes(str)); - js_DropAtom(cx, atom); - ok = JS_FALSE; - goto out; + return JS_FALSE; } - if (!js_LookupProperty(cx, obj, (jsval)atom, &obj, &prop)) { - js_DropAtom(cx, atom); - ok = JS_FALSE; - goto out; + if (!js_LookupProperty(cx, obj, (jsid)atom, &obj2, + (JSProperty **)&sprop)) { + return JS_FALSE; } - if (prop && prop->object == obj) { - PR_ASSERT(prop->getter == js_GetArgument); +#ifdef CHECK_ARGUMENT_HIDING + if (sprop && obj2 == obj) { + PR_ASSERT(sprop->getter == js_GetArgument); + OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop); JS_ReportError(cx, "duplicate formal argument %s", ATOM_BYTES(atom)); - js_DropAtom(cx, atom); - ok = JS_FALSE; - goto out; + return JS_FALSE; } - prop = js_DefineProperty(cx, obj, (jsval)atom, JSVAL_VOID, - js_GetArgument, js_SetArgument, - JSPROP_ENUMERATE | JSPROP_PERMANENT); - js_DropAtom(cx, atom); - if (!prop) { - ok = JS_FALSE; - goto out; +#endif + if (sprop) + OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop); + if (!js_DefineProperty(cx, obj, (jsid)atom, JSVAL_VOID, + js_GetArgument, js_SetArgument, + JSPROP_ENUMERATE | JSPROP_PERMANENT, + (JSProperty **)&sprop)) { + return JS_FALSE; } - prop->id = INT_TO_JSVAL(fun->nargs++); - arg = prop->symbols; - *argp = arg; - argp = &arg->next; + PR_ASSERT(sprop); + sprop->id = INT_TO_JSVAL(fun->nargs++); + OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop); } if (argc) { str = js_ValueToString(cx, argv[argc-1]); - if (!str) { - ok = JS_FALSE; - goto out; - } - js_LockGCThing(cx, str); } else { /* Can't use cx->runtime->emptyString because we're called too early. */ - str = js_NewStringCopyZ(cx, js_empty_ucstr, GCF_LOCK); + str = js_NewStringCopyZ(cx, js_empty_ucstr, 0); } - if ((fp = cx->fp) && (fp = fp->down) && fp->script) { + if (!str) + return JS_FALSE; + if (argv) { + /* Use the last arg (or this if argc == 0) as a local GC root. */ + argv[argc-1] = STRING_TO_JSVAL(str); + } + + if ((fp = cx->fp) != NULL && (fp = fp->down) != NULL && fp->script) { filename = fp->script->filename; lineno = js_PCToLineNumber(fp->script, fp->pc); principals = fp->script->principals; @@ -984,47 +1392,54 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } ts = js_NewTokenStream(cx, str->chars, str->length, filename, lineno, principals); - if (ts) { - ok = js_ParseFunctionBody(cx, ts, fun, args) && - js_CloseTokenStream(cx, ts); - } else { - ok = JS_FALSE; - } - js_UnlockGCThing(cx, str); -out: - JS_UNLOCK(cx); - if (!ok) + if (!ts) return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; + return js_ParseFunctionBody(cx, ts, fun) && + js_CloseTokenStream(cx, ts); } JSObject * js_InitFunctionClass(JSContext *cx, JSObject *obj) { JSObject *proto; - jsval rval; + JSAtom *atom; + JSFunction *fun; proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1, function_props, function_methods, NULL, NULL); - if (!proto || !Function(cx, proto, 0, NULL, &rval)) + if (!proto) return NULL; - - /* Define fun.length as an alias of fun.arity iff < JS1.2. */ - if (cx->version < JSVERSION_1_2 && - !JS_AliasProperty(cx, proto, js_arity_str, js_length_str)) { - return NULL; - } + atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name), + 0); + if (!atom) + goto bad; + fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, atom); + if (!fun) + goto bad; + fun->script = js_NewScript(cx, 0); + if (!fun->script) + goto bad; return proto; + +bad: + cx->newborn[GCX_OBJECT] = NULL; + return NULL; } JSBool -js_InitCallAndClosureClasses(JSContext *cx, JSObject *obj, - JSObject *arrayProto) +js_InitArgsCallClosureClasses(JSContext *cx, JSObject *obj, + JSObject *arrayProto) { +#if JS_HAS_ARGS_OBJECT + if (!JS_InitClass(cx, obj, arrayProto, &js_ArgumentsClass, Arguments, 0, + args_props, NULL, NULL, NULL)) { + return JS_FALSE; + } +#endif + #if JS_HAS_CALL_OBJECT - if (!JS_InitClass(cx, obj, arrayProto, &js_CallClass, Call, 0, - call_props, call_methods, NULL, NULL)) { + if (!JS_InitClass(cx, obj, NULL, &js_CallClass, Call, 0, + call_props, NULL, NULL, NULL)) { return JS_FALSE; } #endif @@ -1040,55 +1455,114 @@ js_InitCallAndClosureClasses(JSContext *cx, JSObject *obj, } JSFunction * -js_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, - JSObject *parent, JSAtom *atom) +js_NewFunction(JSContext *cx, JSObject *funobj, JSNative call, uintN nargs, + uintN flags, JSObject *parent, JSAtom *atom) { - return NewFunction(cx, NULL, call, nargs, flags, parent, atom); + JSFunction *fun; + + /* Allocate a function struct. */ + fun = JS_malloc(cx, sizeof *fun); + if (!fun) + return NULL; + fun->nrefs = 0; + fun->object = NULL; + + /* If funobj is null, allocate an object for it. */ + if (!funobj) { + funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); + if (!funobj) { + JS_free(cx, fun); + return NULL; + } + } else { + OBJ_SET_PARENT(cx, funobj, parent); + } + + /* Link fun to funobj and vice versa. */ + if (!js_LinkFunctionObject(cx, fun, funobj)) { + cx->newborn[GCX_OBJECT] = NULL; + JS_free(cx, fun); + return NULL; + } + + /* Initialize remaining function members. */ + fun->call = call; + fun->nargs = nargs; + fun->flags = flags & JSFUN_FLAGS_MASK; + fun->extra = 0; + fun->nvars = 0; + fun->spare = 0; + fun->atom = atom; + fun->script = NULL; + return fun; +} + +JSBool +js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object) +{ + if (!fun->object) + fun->object = object; + if (!JS_SetPrivate(cx, object, fun)) + return JS_FALSE; + JS_ATOMIC_ADDREF(&fun->nrefs, 1); + return JS_TRUE; } JSFunction * js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative call, - uintN nargs, uintN flags) + uintN nargs, uintN attrs) { JSFunction *fun; - fun = js_NewFunction(cx, call, nargs, flags, obj, atom); + fun = js_NewFunction(cx, NULL, call, nargs, attrs, obj, atom); if (!fun) return NULL; - if (!js_DefineProperty(cx, obj, (jsval)atom, OBJECT_TO_JSVAL(fun->object), - NULL, NULL, flags)) { + if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(fun->object), + NULL, NULL, attrs, NULL)) { return NULL; } return fun; } JSFunction * -js_ValueToFunction(JSContext *cx, jsval v) +js_ValueToFunction(JSContext *cx, jsval *vp, JSBool constructing) { + jsval v; JSObject *obj; - jsval rval; - JSString *str; + v = *vp; obj = NULL; if (JSVAL_IS_OBJECT(v)) { obj = JSVAL_TO_OBJECT(v); - if (obj && obj->map->clasp != &js_FunctionClass) { - rval = JSVAL_VOID; - if (!obj->map->clasp->convert(cx, obj, JSTYPE_FUNCTION, &rval)) + if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v)) return NULL; - if (JSVAL_IS_FUNCTION(rval)) - obj = JSVAL_TO_OBJECT(rval); - else - obj = NULL; + obj = JSVAL_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL; } } if (!obj) { - str = js_ValueToSource(cx, v); - if (str) { - JS_ReportError(cx, "%s is not a function", - JS_GetStringBytes(str)); - } + js_ReportIsNotFunction(cx, vp, constructing); return NULL; } return JS_GetPrivate(cx, obj); } + +void +js_ReportIsNotFunction(JSContext *cx, jsval *vp, JSBool constructing) +{ + JSStackFrame *fp; + jsval *sp; + JSString *str; + + fp = cx->fp; + if (fp) + sp = fp->sp, fp->sp = vp; + str = js_DecompileValueGenerator(cx, *vp, NULL); + if (fp) + fp->sp = sp; + if (str) { + JS_ReportError(cx, "%s is not a %s", + JS_GetStringBytes(str), + constructing ? "constructor" : "function"); + } +} diff --git a/mozilla/js/src/jsfun.h b/mozilla/js/src/jsfun.h index 8fa15835159..480a31347ed 100644 --- a/mozilla/js/src/jsfun.h +++ b/mozilla/js/src/jsfun.h @@ -27,15 +27,19 @@ PR_BEGIN_EXTERN_C 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 extra; /* number of arg slots for local GC roots */ uint16 nvars; /* number of local variables */ + uint16 spare; /* reserved for future use */ JSAtom *atom; /* name for diagnostics and decompiling */ JSScript *script; /* interpreted bytecode descriptor or null */ }; +extern JSClass js_ArgumentsClass; extern JSClass js_CallClass; extern JSClass js_ClosureClass; extern JSClass js_FunctionClass; @@ -43,9 +47,9 @@ extern JSClass js_FunctionClass; /* * NB: jsapi.h and jsobj.h must be included before any call to this macro. */ -#define JSVAL_IS_FUNCTION(v) \ +#define JSVAL_IS_FUNCTION(cx, v) \ (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) && \ - JSVAL_TO_OBJECT(v)->map->clasp == &js_FunctionClass) + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) extern JSBool js_IsIdentifier(JSString *str); @@ -54,31 +58,41 @@ extern JSObject * js_InitFunctionClass(JSContext *cx, JSObject *obj); extern JSBool -js_InitCallAndClosureClasses(JSContext *cx, JSObject *obj, - JSObject *arrayProto); +js_InitArgsCallClosureClasses(JSContext *cx, JSObject *obj, + JSObject *arrayProto); extern JSFunction * -js_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, - JSObject *parent, JSAtom *atom); +js_NewFunction(JSContext *cx, JSObject *funobj, JSNative call, uintN nargs, + uintN flags, JSObject *parent, JSAtom *atom); + +extern JSBool +js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object); extern JSFunction * js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative call, uintN nargs, uintN flags); extern JSFunction * -js_ValueToFunction(JSContext *cx, jsval v); +js_ValueToFunction(JSContext *cx, jsval *vp, JSBool constructing); + +extern void +js_ReportIsNotFunction(JSContext *cx, jsval *vp, JSBool constructing); extern JSObject * -js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent); +js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent, + JSObject *withobj); extern JSBool js_PutCallObject(JSContext *cx, JSStackFrame *fp); -extern JSBool -js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); +extern JSObject * +js_GetArgsObject(JSContext *cx, JSStackFrame *fp); extern JSBool -js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); +js_PutArgsObject(JSContext *cx, JSStackFrame *fp); + +extern JSBool +js_XDRFunction(JSXDRState *xdr, JSObject **objp); PR_END_EXTERN_C diff --git a/mozilla/js/src/jsgc.c b/mozilla/js/src/jsgc.c index c06ede51528..06e53d9d96b 100644 --- a/mozilla/js/src/jsgc.c +++ b/mozilla/js/src/jsgc.c @@ -43,12 +43,14 @@ #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" +#include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsscope.h" +#include "jsscript.h" #include "jsstr.h" /* @@ -147,29 +149,26 @@ js_FinishGC(JSRuntime *rt) } JSBool -js_AddRoot(JSContext *cx, void *rp) +js_AddRoot(JSContext *cx, void *rp, const char *name) { - if (!PR_HashTableAdd(cx->runtime->gcRootsHash, rp, NULL)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - return JS_TRUE; -} + JSRuntime *rt; + JSBool ok; -JSBool -js_AddNamedRoot(JSContext *cx, void *rp, const char *name) -{ - if (!PR_HashTableAdd(cx->runtime->gcRootsHash, rp, (void *)name)) { + rt = cx->runtime; + JS_LOCK_GC_VOID(rt, + ok = (PR_HashTableAdd(rt->gcRootsHash, rp, (void *)name) != NULL)); + if (!ok) JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - return JS_TRUE; + return ok; } JSBool js_RemoveRoot(JSContext *cx, void *rp) { - (void) PR_HashTableRemove(cx->runtime->gcRootsHash, rp); + JSRuntime *rt; + + rt = cx->runtime; + JS_LOCK_GC_VOID(rt, PR_HashTableRemove(rt->gcRootsHash, rp)); return JS_TRUE; } @@ -187,7 +186,7 @@ js_AllocGCThing(JSContext *cx, uintN flags) #endif rt = cx->runtime; - JS_LOCK_RUNTIME(rt); + JS_LOCK_GC(rt); METER(rt->gcStats.alloc++); retry: thing = rt->gcFreeList; @@ -205,14 +204,16 @@ retry: if (thing) PR_ARENA_RELEASE(&rt->gcArenaPool, thing); if (!tried_gc) { + JS_UNLOCK_GC(rt); js_GC(cx); tried_gc = JS_TRUE; + JS_LOCK_GC(rt); METER(rt->gcStats.retry++); goto retry; } - JS_ReportOutOfMemory(cx); METER(rt->gcStats.fail++); - JS_UNLOCK_RUNTIME(rt); + JS_UNLOCK_GC(rt); + JS_ReportOutOfMemory(cx); return NULL; } } @@ -226,7 +227,7 @@ retry: */ thing->next = NULL; thing->flagp = NULL; - JS_UNLOCK_RUNTIME(rt); + JS_UNLOCK_GC(rt); return thing; } @@ -301,8 +302,8 @@ js_UnlockGCThing(JSContext *cx, void *thing) #include #include "prprf.h" -FILE *js_DumpGCHeap; -void *js_LiveThingToFind; +JS_FRIEND_DATA(FILE *) js_DumpGCHeap; +JS_FRIEND_DATA(void *) js_LiveThingToFind; typedef struct GCMarkNode GCMarkNode; @@ -318,7 +319,9 @@ gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp) { GCMarkNode *next = NULL; char *path = NULL; - + JSObject *obj; + JSClass *clasp; + while (prev) { next = prev; prev = prev->prev; @@ -333,13 +336,15 @@ gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp) fprintf(fp, "%08lx ", (long)thing); switch (flags & GCF_TYPEMASK) { case GCX_OBJECT: - fprintf(fp, "class %s", ((JSObject *)thing)->map->clasp->name); + obj = (JSObject *)thing; + clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]); + fprintf(fp, "object %s", clasp->name); break; case GCX_STRING: - fprintf(fp, "bytes %s", JS_GetStringBytes((JSString *)thing)); + fprintf(fp, "string %s", JS_GetStringBytes((JSString *)thing)); break; case GCX_DOUBLE: - fprintf(fp, "value %g", *(jsdouble *)thing); + fprintf(fp, "double %g", *(jsdouble *)thing); break; case GCX_DECIMAL: break; @@ -351,15 +356,16 @@ gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp) static void gc_mark_node(JSRuntime *rt, void *thing, GCMarkNode *prev); -#define GC_MARK(_rt, _thing, _name, _prev) { \ - GCMarkNode _node; \ - _node.thing = _thing; \ - _node.name = _name; \ - _node.next = NULL; \ - _node.prev = _prev; \ - if (_prev) ((GCMarkNode *)(_prev))->next = &_node; \ - gc_mark_node(_rt, _thing, &_node); \ -} +#define GC_MARK(_rt, _thing, _name, _prev) \ + PR_BEGIN_MACRO \ + GCMarkNode _node; \ + _node.thing = _thing; \ + _node.name = _name; \ + _node.next = NULL; \ + _node.prev = _prev; \ + if (_prev) ((GCMarkNode *)(_prev))->next = &_node; \ + gc_mark_node(_rt, _thing, &_node); \ + PR_END_MACRO static void gc_mark(JSRuntime *rt, void *thing) @@ -367,22 +373,82 @@ gc_mark(JSRuntime *rt, void *thing) GC_MARK(rt, thing, "atom", NULL); } +#define GC_MARK_ATOM(rt, atom, prev) gc_mark_atom(rt, atom, prev) +#define GC_MARK_SCRIPT(rt, script, prev) gc_mark_script(rt, script, prev) + +#else /* !GC_MARK_DEBUG */ + +#define GC_MARK(rt, thing, name, prev) gc_mark(rt, thing) +#define GC_MARK_ATOM(rt, atom, prev) gc_mark_atom(rt, atom) +#define GC_MARK_SCRIPT(rt, script, prev) gc_mark_script(rt, script) + static void +gc_mark(JSRuntime *rt, void *thing); + +#endif /* !GC_MARK_DEBUG */ + +static void +gc_mark_atom(JSRuntime *rt, JSAtom *atom +#ifdef GC_MARK_DEBUG + , GCMarkNode *prev +#endif +) +{ + jsval key; + + if (!atom || atom->flags & ATOM_MARK) + return; + atom->flags |= ATOM_MARK; + key = ATOM_KEY(atom); + if (JSVAL_IS_GCTHING(key)) { +#ifdef GC_MARK_DEBUG + char name[32]; + + if (JSVAL_IS_STRING(key)) { + PR_snprintf(name, sizeof name, "'%s'", + JS_GetStringBytes(JSVAL_TO_STRING(key))); + } else { + PR_snprintf(name, sizeof name, "<%x>", key); + } +#endif + GC_MARK(rt, JSVAL_TO_GCTHING(key), name, prev); + } +} + +static void +gc_mark_script(JSRuntime *rt, JSScript *script +#ifdef GC_MARK_DEBUG + , GCMarkNode *prev +#endif +) +{ + JSAtomMap *map; + uintN i, length; + JSAtom **vector; + + map = &script->atomMap; + length = map->length; + vector = map->vector; + for (i = 0; i < length; i++) + GC_MARK_ATOM(rt, vector[i], prev); +} + +static void +#ifdef GC_MARK_DEBUG gc_mark_node(JSRuntime *rt, void *thing, GCMarkNode *prev) - -#else /* GC_MARK_DEBUG */ - -#define GC_MARK(rt, thing, name, prev) gc_mark(rt, thing) - -static void +#else gc_mark(JSRuntime *rt, void *thing) - -#endif /* GC_MARK_DEBUG */ +#endif { uint8 flags, *flagp; JSObject *obj; jsval v, *vp, *end; JSScope *scope; + JSClass *clasp; + JSScript *script; + JSFunction *fun; + JSScopeProperty *sprop; + JSSymbol *sym; if (!thing) return; @@ -390,7 +456,11 @@ gc_mark(JSRuntime *rt, void *thing) if (!flagp) return; + /* Check for something on the GC freelist to handle recycled stack. */ flags = *flagp; + if (flags == GCF_FINAL) + return; + #ifdef GC_MARK_DEBUG if (js_LiveThingToFind == thing) gc_dump_thing(thing, flags, prev, stderr); @@ -411,8 +481,41 @@ gc_mark(JSRuntime *rt, void *thing) obj = thing; vp = obj->slots; if (vp) { - scope = (JSScope *) obj->map; - if (scope->object == obj) + scope = OBJ_IS_NATIVE(obj) ? (JSScope *) obj->map : NULL; + if (scope) { + clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]); + + if (clasp == &js_ScriptClass) { + v = vp[JSSLOT_PRIVATE]; + if (!JSVAL_IS_VOID(v)) { + script = JSVAL_TO_PRIVATE(v); + if (script) + GC_MARK_SCRIPT(rt, script, prev); + } + } + + if (clasp == &js_FunctionClass) { + v = vp[JSSLOT_PRIVATE]; + if (!JSVAL_IS_VOID(v)) { + fun = JSVAL_TO_PRIVATE(v); + if (fun) { + if (fun->atom) + GC_MARK_ATOM(rt, fun->atom, prev); + if (fun->script) + GC_MARK_SCRIPT(rt, fun->script, prev); + } + } + } + + for (sprop = scope->props; sprop; sprop = sprop->next) { + for (sym = sprop->symbols; sym; sym = sym->next) { + if (JSVAL_IS_INT(sym_id(sym))) + continue; + GC_MARK_ATOM(rt, sym_atom(sym), prev); + } + } + } + if (!scope || scope->object == obj) end = vp + obj->map->freeslot; else end = vp + JS_INITIAL_NSLOTS; @@ -420,44 +523,48 @@ gc_mark(JSRuntime *rt, void *thing) v = *vp; if (JSVAL_IS_GCTHING(v)) { #ifdef GC_MARK_DEBUG - uint32 slot; - JSProperty *prop; - jsval nval; char name[32]; - slot = vp - obj->slots; - for (prop = scope->map.props; ; prop = prop->next) { - if (!prop) { - switch (slot) { - case JSSLOT_PROTO: - strcpy(name, "__proto__"); - break; - case JSSLOT_PARENT: - strcpy(name, "__parent__"); - break; - case JSSLOT_PRIVATE: - strcpy(name, "__private__"); - break; - default: - strcpy(name, "**UNKNOWN SLOT**"); + if (scope) { + uint32 slot; + jsval nval; + + slot = vp - obj->slots; + for (sprop = scope->props; ; sprop = sprop->next) { + if (!sprop) { + switch (slot) { + case JSSLOT_PROTO: + strcpy(name, "__proto__"); + break; + case JSSLOT_PARENT: + strcpy(name, "__parent__"); + break; + case JSSLOT_PRIVATE: + strcpy(name, "__private__"); + break; + default: + PR_snprintf(name, sizeof name, + "**UNKNOWN SLOT %ld**", + (long)slot); + break; + } break; } - break; - } - if (prop->slot == slot) { - nval = prop->symbols - ? js_IdToValue(sym_id(prop->symbols)) - : prop->id; - if (JSVAL_IS_INT(nval)) { - PR_snprintf(name, sizeof name, "%ld", - (long)JSVAL_TO_INT(nval)); - } else if (JSVAL_IS_STRING(nval)) { - PR_snprintf(name, sizeof name, "%s", - JS_GetStringBytes(JSVAL_TO_STRING(nval))); - } else { - strcpy(name, "**FINALIZED ATOM KEY**"); + if (sprop->slot == slot) { + nval = sprop->symbols + ? js_IdToValue(sym_id(sprop->symbols)) + : sprop->id; + if (JSVAL_IS_INT(nval)) { + PR_snprintf(name, sizeof name, "%ld", + (long)JSVAL_TO_INT(nval)); + } else if (JSVAL_IS_STRING(nval)) { + PR_snprintf(name, sizeof name, "%s", + JS_GetStringBytes(JSVAL_TO_STRING(nval))); + } else { + strcpy(name, "**FINALIZED ATOM KEY**"); + } + break; } - break; } } #endif @@ -479,7 +586,7 @@ gc_hash_root(const void *key) } PR_STATIC_CALLBACK(intN) -gc_root_enumerator(PRHashEntry *he, intN i, void *arg) +gc_root_marker(PRHashEntry *he, intN i, void *arg) { void **rp = (void **)he->key; @@ -488,10 +595,9 @@ gc_root_enumerator(PRHashEntry *he, intN i, void *arg) return HT_ENUMERATE_NEXT; } -void +JS_FRIEND_API(void) js_ForceGC(JSContext *cx) { - PR_ASSERT(JS_IS_LOCKED(cx)); cx->newborn[GCX_OBJECT] = NULL; cx->newborn[GCX_STRING] = NULL; cx->newborn[GCX_DOUBLE] = NULL; @@ -505,26 +611,78 @@ js_GC(JSContext *cx) { JSRuntime *rt; JSContext *iter, *acx; - PRArena *a, *fa, **ap, **fap; + PRArena *a, *ma, *fa, **ap, **fap; jsval v, *vp, *sp; pruword begin, end; JSStackFrame *fp; + void *mark; uint8 flags, *flagp; - JSGCThing *thing, **flp, **oflp; + JSGCThing *thing, *final, **flp, **oflp; GCFinalizeOp finalizer; JSBool a_all_clear, f_all_clear; rt = cx->runtime; - PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); +#ifdef JS_THREADSAFE + /* Avoid deadlock. */ + PR_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); +#endif + + /* Let the API user decide to defer a GC if it wants to. */ + if (rt->gcCallback && !rt->gcCallback(cx, JSGC_BEGIN)) + return; + + /* Lock out other GC allocator and collector invocations. */ + JS_LOCK_GC(rt); /* Do nothing if no assignment has executed since the last GC. */ if (!rt->gcPoke) { METER(rt->gcStats.nopoke++); + JS_UNLOCK_GC(rt); return; } rt->gcPoke = JS_FALSE; METER(rt->gcStats.poke++); +#ifdef JS_THREADSAFE + /* Bump gcLevel and return rather than nest on this context. */ + if (cx->gcActive) { + rt->gcLevel++; + METER(if (rt->gcLevel > rt->gcStats.maxlevel) + rt->gcStats.maxlevel = rt->gcLevel); + if (rt->gcLevel > 1) { + JS_UNLOCK_GC(rt); + return; + } + } + + /* If we're in a request, indicate, temporarily, that we're inactive. */ + if (cx->requestDepth) { + rt->requestCount--; + JS_NOTIFY_REQUEST_DONE(rt); + } + + /* If another thread is already in GC, don't attempt GC; wait instead. */ + if (rt->gcLevel > 0) { + while (rt->gcLevel > 0) + JS_AWAIT_GC_DONE(rt); + if (cx->requestDepth) + rt->requestCount++; + JS_UNLOCK_GC(rt); + return; + } + + /* No other thread is in GC, so indicate that we're now in GC. */ + rt->gcLevel = 1; + + /* Also indicate that GC is active on this context. */ + cx->gcActive = JS_TRUE; + + /* Wait for all other requests to finish. */ + while (rt->requestCount > 0) + JS_AWAIT_REQUEST_DONE(rt); + +#else /* !JS_THREADSAFE */ + /* Bump gcLevel and return rather than nest; the outer gc will restart. */ rt->gcLevel++; METER(if (rt->gcLevel > rt->gcStats.maxlevel) @@ -532,14 +690,16 @@ js_GC(JSContext *cx) if (rt->gcLevel > 1) return; +#endif /* !JS_THREADSAFE */ + /* Drop atoms held by the property cache, and clear property weak links. */ js_FlushPropertyCache(cx); restart: rt->gcNumber++; /* Mark phase. */ - PR_HashTableEnumerateEntries(rt->gcRootsHash, gc_root_enumerator, rt); - js_MarkAtomState(rt, gc_mark); + PR_HashTableEnumerateEntries(rt->gcRootsHash, gc_root_marker, rt); + js_MarkAtomState(&rt->atomState, gc_mark); iter = NULL; while ((acx = js_ContextIterator(rt, &iter)) != NULL) { fp = acx->fp; @@ -549,15 +709,15 @@ restart: for (a = acx->stackPool.first.next; a; a = a->next) { begin = a->base; end = a->avail; -#ifndef JS_THREADSAFE if (PR_UPTRDIFF(sp, begin) < PR_UPTRDIFF(end, begin)) end = (pruword)sp; -#endif for (vp = (jsval *)begin; vp < (jsval *)end; vp++) { v = *vp; if (JSVAL_IS_GCTHING(v)) GC_MARK(rt, JSVAL_TO_GCTHING(v), "stack", NULL); } + if (end == (pruword)sp) + break; } } do { @@ -565,12 +725,14 @@ restart: GC_MARK(rt, fp->thisp, "this", NULL); if (JSVAL_IS_GCTHING(fp->rval)) GC_MARK(rt, JSVAL_TO_GCTHING(fp->rval), "rval", NULL); - if (fp->object) - GC_MARK(rt, fp->object, "call object", NULL); -#if JS_HAS_SHARP_VARS + if (fp->callobj) + GC_MARK(rt, fp->callobj, "call object", NULL); + if (fp->argsobj) + GC_MARK(rt, fp->argsobj, "arguments object", NULL); + if (fp->script) + GC_MARK_SCRIPT(rt, fp->script, NULL); if (fp->sharpArray) GC_MARK(rt, fp->sharpArray, "sharp array", NULL); -#endif } while ((fp = fp->down) != NULL); } GC_MARK(rt, acx->globalObject, "global object", NULL); @@ -579,7 +741,10 @@ restart: GC_MARK(rt, acx->newborn[GCX_DOUBLE], "newborn double", NULL); } - /* Sweep phase. */ + /* Sweep phase. Mark in tempPool for release at label out:. */ + ma = cx->tempPool.current; + mark = PR_ARENA_MARK(&cx->tempPool); + js_SweepAtomState(&rt->atomState); fa = rt->gcFlagsPool.first.next; flagp = (uint8 *)fa->base; for (a = rt->gcArenaPool.first.next; a; a = a->next) { @@ -598,25 +763,45 @@ restart: if (flags & GCF_MARK) { *flagp &= ~GCF_MARK; } else if (!(flags & (GCF_LOCKMASK | GCF_FINAL))) { - finalizer = gc_finalizers[flags & GCF_TYPEMASK]; - if (finalizer) { - *flagp |= GCF_FINAL; - finalizer(cx, thing); - } - - /* - * Set flags to GCF_FINAL, signifying that thing is free, - * but don't thread thing onto rt->gcFreeList. We rebuild - * the freelist below while looking for free-able arenas. - */ - *flagp = GCF_FINAL; - PR_ASSERT(rt->gcBytes >= sizeof(JSGCThing)+sizeof(uint8)); + PR_ARENA_ALLOCATE(final, &cx->tempPool, sizeof(JSGCThing)); + if (!final) + goto out; + final->next = thing; + final->flagp = flagp; + PR_ASSERT(rt->gcBytes >= sizeof(JSGCThing) + sizeof(uint8)); rt->gcBytes -= sizeof(JSGCThing) + sizeof(uint8); } flagp++; } } + /* Finalize phase. Don't hold the GC lock while running finalizers! */ + JS_UNLOCK_GC(rt); + for (final = mark; ; final++) { + if ((pruword)final >= ma->avail) { + ma = ma->next; + if (!ma) + break; + final = (JSGCThing *)ma->base; + } + thing = final->next; + flagp = final->flagp; + flags = *flagp; + finalizer = gc_finalizers[flags & GCF_TYPEMASK]; + if (finalizer) { + *flagp |= GCF_FINAL; + finalizer(cx, thing); + } + + /* + * Set flags to GCF_FINAL, signifying that thing is free, but don't + * thread thing onto rt->gcFreeList. We need the GC lock to rebuild + * the freelist below while also looking for free-able arenas. + */ + *flagp = GCF_FINAL; + } + JS_LOCK_GC(rt); + /* Free unused arenas and rebuild the freelist. */ ap = &rt->gcArenaPool.first.next; a = *ap; @@ -677,10 +862,22 @@ restart: *flp = NULL; out: + PR_ARENA_RELEASE(&cx->tempPool, mark); if (rt->gcLevel > 1) { rt->gcLevel = 1; goto restart; } rt->gcLevel = 0; rt->gcLastBytes = rt->gcBytes; + +#ifdef JS_THREADSAFE + /* If we were invoked during a request, undo the temporary decrement. */ + if (cx->requestDepth) + rt->requestCount++; + cx->gcActive = JS_FALSE; + JS_NOTIFY_GC_DONE(rt); + JS_UNLOCK_GC(rt); +#endif + if (rt->gcCallback) + (void) rt->gcCallback(cx, JSGC_END); } diff --git a/mozilla/js/src/jsgc.h b/mozilla/js/src/jsgc.h index 09387e62efa..1ab21033909 100644 --- a/mozilla/js/src/jsgc.h +++ b/mozilla/js/src/jsgc.h @@ -57,10 +57,7 @@ extern void js_FinishGC(JSRuntime *rt); extern JSBool -js_AddRoot(JSContext *cx, void *rp); - -extern JSBool -js_AddNamedRoot(JSContext *cx, void *rp, const char *name); +js_AddRoot(JSContext *cx, void *rp, const char *name); extern JSBool js_RemoveRoot(JSContext *cx, void *rp); @@ -74,7 +71,7 @@ js_LockGCThing(JSContext *cx, void *thing); extern JSBool js_UnlockGCThing(JSContext *cx, void *thing); -extern void +extern JS_FRIEND_API(void) js_ForceGC(JSContext *cx); extern void diff --git a/mozilla/js/src/jsinterp.c b/mozilla/js/src/jsinterp.c index 8c06ed94f2f..4c812d122b9 100644 --- a/mozilla/js/src/jsinterp.c +++ b/mozilla/js/src/jsinterp.c @@ -46,21 +46,11 @@ void js_FlushPropertyCache(JSContext *cx) { JSPropertyCache *cache; - JSPropertyCacheEntry *end, *pce; cache = &cx->runtime->propertyCache; if (cache->empty) return; - - end = &cache->table[PROPERTY_CACHE_SIZE]; - for (pce = &cache->table[0]; pce < end; pce++) { - if (pce->property) { - pce->property = NULL; - if (!JSVAL_IS_INT(pce->symbolid)) - js_DropAtom(cx, (JSAtom *)pce->symbolid); - pce->symbolid = JSVAL_NULL; - } - } + memset(cache->table, 0, sizeof cache->table); cache->empty = JS_TRUE; cache->flushes++; } @@ -70,7 +60,8 @@ js_FlushPropertyCacheByProp(JSContext *cx, JSProperty *prop) { JSPropertyCache *cache; JSBool empty; - JSPropertyCacheEntry *end, *pce; + JSPropertyCacheEntry *end, *pce, entry; + JSProperty *pce_prop; cache = &cx->runtime->propertyCache; if (cache->empty) @@ -79,37 +70,35 @@ js_FlushPropertyCacheByProp(JSContext *cx, JSProperty *prop) empty = JS_TRUE; end = &cache->table[PROPERTY_CACHE_SIZE]; for (pce = &cache->table[0]; pce < end; pce++) { - if (pce->property) { - if (pce->property == prop) { - pce->property = NULL; - if (!JSVAL_IS_INT(pce->symbolid)) - js_DropAtom(cx, (JSAtom *)pce->symbolid); - pce->symbolid = JSVAL_NULL; + PCE_LOAD(cache, pce, entry); + pce_prop = PCE_PROPERTY(entry); + if (pce_prop) { + if (pce_prop == prop) { + PCE_OBJECT(entry) = NULL; + PCE_PROPERTY(entry) = NULL; + PCE_STORE(cache, pce, entry); } else { empty = JS_FALSE; } } } cache->empty = empty; - cache->pflushes++; } /* * Class for for/in loop property iterator objects. */ -#define JSSLOT_PROP_OBJECT (JSSLOT_START) -#define JSSLOT_PROP_NEXT (JSSLOT_START+1) +#define JSSLOT_ITR_STATE (JSSLOT_START) static void prop_iterator_finalize(JSContext *cx, JSObject *obj) { - jsval pval; - JSProperty *prop; + jsval iter_state; - pval = OBJ_GET_SLOT(obj, JSSLOT_PROP_NEXT); - prop = JSVAL_TO_PRIVATE(pval); - if (prop) - js_DropProperty(cx, prop); + /* Protect against stillborn iterators. */ + iter_state = obj->slots[JSSLOT_ITR_STATE]; + if (iter_state) + OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, NULL); } static JSClass prop_iterator_class = { @@ -169,16 +158,15 @@ static JSClass prop_iterator_class = { PR_END_MACRO /* - * This POP variant is called only for bitwise operators, so we don't bother - * to inline it. The calls in Interpret must therefore SAVE_SP first! + * This POP variant is called only for bitwise operators and for tableswitch, + * so we don't bother to inline it. The calls in Interpret must therefore + * SAVE_SP first! */ static JSBool -PopInt(JSContext *cx, jsint *ip, JSBool *validp) +PopInt(JSContext *cx, jsint *ip) { JSStackFrame *fp; jsval *sp, v; - jsdouble d; - jsint i; fp = cx->fp; RESTORE_SP(fp); @@ -188,18 +176,25 @@ PopInt(JSContext *cx, jsint *ip, JSBool *validp) *ip = JSVAL_TO_INT(v); return JS_TRUE; } - if (!JS_ValueToNumber(cx, v, &d)) - return JS_FALSE; - i = (jsint)d; - *ip = i; -#ifdef XP_PC - if (JSDOUBLE_IS_NaN(d)) { - *validp = JS_FALSE; + return js_ValueToECMAInt32(cx, v, (int32 *)ip); +} + +static JSBool +PopUint(JSContext *cx, jsuint *ip) +{ + JSStackFrame *fp; + jsval *sp, v; + jsint i; + + fp = cx->fp; + RESTORE_SP(fp); + v = POP(); + SAVE_SP(fp); + if (JSVAL_IS_INT(v) && (i = JSVAL_TO_INT(v)) >= 0) { + *ip = i; return JS_TRUE; } -#endif - *validp &= ((jsdouble)i == d || (jsdouble)(jsuint)i == d); - return JS_TRUE; + return js_ValueToECMAUint32(cx, v, (uint32 *)ip); } /* @@ -214,21 +209,22 @@ PopInt(JSContext *cx, jsint *ip, JSBool *validp) d = *JSVAL_TO_DOUBLE(v); \ } else { \ SAVE_SP(fp); \ - JS_LOCK_RUNTIME_VOID(rt, ok = js_ValueToNumber(cx, v, &d)); \ + ok = js_ValueToNumber(cx, v, &d); \ if (!ok) \ goto out; \ } \ PR_END_MACRO -#define VALUE_TO_BOOLEAN(cx, v, b) \ +#define POP_BOOLEAN(cx, v, b) \ PR_BEGIN_MACRO \ + v = POP(); \ if (v == JSVAL_NULL) { \ b = JS_FALSE; \ } else if (JSVAL_IS_BOOLEAN(v)) { \ b = JSVAL_TO_BOOLEAN(v); \ } else { \ SAVE_SP(fp); \ - JS_LOCK_RUNTIME_VOID(rt, ok = js_ValueToBoolean(cx, v, &b)); \ + ok = js_ValueToBoolean(cx, v, &b); \ if (!ok) \ goto out; \ } \ @@ -240,7 +236,7 @@ PopInt(JSContext *cx, jsint *ip, JSBool *validp) obj = JSVAL_TO_OBJECT(v); \ } else { \ SAVE_SP(fp); \ - JS_LOCK_RUNTIME_VOID(rt, obj = js_ValueToNonNullObject(cx, v)); \ + obj = js_ValueToNonNullObject(cx, v); \ if (!obj) { \ ok = JS_FALSE; \ goto out; \ @@ -249,51 +245,43 @@ PopInt(JSContext *cx, jsint *ip, JSBool *validp) PR_END_MACRO #if JS_BUG_VOID_TOSTRING -#define CHECK_VOID_TOSTRING(cx, v, vp) \ +#define CHECK_VOID_TOSTRING(cx, v) \ if (JSVAL_IS_VOID(v)) { \ + JSString *_str; \ _str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); \ - *vp = STRING_TO_JSVAL(_str); \ + v = STRING_TO_JSVAL(_str); \ } #else -#define CHECK_VOID_TOSTRING(cx, v, vp) /* nothing */ +#define CHECK_VOID_TOSTRING(cx, v) ((void)0) #endif #if JS_BUG_EAGER_TOSTRING -#define TRY_VALUE_OF(cx, v, vp, TOSTRING_CODE) TOSTRING_CODE +#define CHECK_EAGER_TOSTRING(hint) (hint = JSTYPE_STRING) #else -#define TRY_VALUE_OF(cx, v, vp, TOSTRING_CODE) \ - js_TryValueOf(cx, JSVAL_TO_OBJECT(v), JSTYPE_VOID, vp); \ - v = *vp; \ - if (JSVAL_IS_OBJECT(v) && v != JSVAL_NULL) { \ - TOSTRING_CODE \ - } +#define CHECK_EAGER_TOSTRING(hint) ((void)0) #endif -#define VALUE_TO_PRIMITIVE(cx, v, vp) \ +#define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \ PR_BEGIN_MACRO \ - JSString *_str; \ - \ - if (!JSVAL_IS_OBJECT(v) || v == JSVAL_NULL) { \ - CHECK_VOID_TOSTRING(cx, v, vp); \ + if (JSVAL_IS_PRIMITIVE(v)) { \ + CHECK_VOID_TOSTRING(cx, v); \ + *vp = v; \ } else { \ - SAVE_SP(cx->fp); \ - TRY_VALUE_OF(cx, v, vp, \ - JS_LOCK_VOID(cx, \ - _str = js_ObjectToString(cx, JSVAL_TO_OBJECT(v))); \ - if (!_str) { \ - ok = JS_FALSE; \ - goto out; \ - } \ - *vp = STRING_TO_JSVAL(_str); \ - ) \ + SAVE_SP(fp); \ + CHECK_EAGER_TOSTRING(hint); \ + ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \ + if (!ok) \ + goto out; \ } \ PR_END_MACRO -static jsval * -AllocStack(JSContext *cx, uintN nslots) +jsval * +js_AllocStack(JSContext *cx, uintN nslots, void **markp) { jsval *sp; + if (markp) + *markp = PR_ARENA_MARK(&cx->stackPool); PR_ARENA_ALLOCATE(sp, &cx->stackPool, nslots * sizeof(jsval)); if (!sp) { JS_ReportError(cx, "stack overflow in %s", @@ -304,15 +292,24 @@ AllocStack(JSContext *cx, uintN nslots) return sp; } +void +js_FreeStack(JSContext *cx, void *mark) +{ + PR_ARENA_RELEASE(&cx->stackPool, mark); +} + JSBool js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { + JSFunction *fun; JSStackFrame *fp; + PR_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); + fun = JS_GetPrivate(cx, obj); for (fp = cx->fp; fp; fp = fp->down) { /* Find most recent non-native function frame. */ if (fp->fun && !fp->fun->call) { - if (fp->fun->object == obj) { + if (fp->fun == fun) { PR_ASSERT((uintN)JSVAL_TO_INT(id) < fp->fun->nargs); *vp = fp->argv[JSVAL_TO_INT(id)]; } @@ -325,12 +322,15 @@ js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSBool js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { + JSFunction *fun; JSStackFrame *fp; + PR_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); + fun = JS_GetPrivate(cx, obj); for (fp = cx->fp; fp; fp = fp->down) { /* Find most recent non-native function frame. */ if (fp->fun && !fp->fun->call) { - if (fp->fun->object == obj) { + if (fp->fun == fun) { PR_ASSERT((uintN)JSVAL_TO_INT(id) < fp->fun->nargs); fp->argv[JSVAL_TO_INT(id)] = *vp; } @@ -343,13 +343,16 @@ js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSBool js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { + JSFunction *fun; JSStackFrame *fp; jsint slot; + PR_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); + fun = JS_GetPrivate(cx, obj); for (fp = cx->fp; fp; fp = fp->down) { /* Find most recent non-native function frame. */ if (fp->fun && !fp->fun->call) { - if (fp->fun->object == obj) { + if (fp->fun == fun) { slot = JSVAL_TO_INT(id); PR_ASSERT((uintN)slot < fp->fun->nvars); if ((uintN)slot < fp->nvars) @@ -364,13 +367,16 @@ js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSBool js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { + JSFunction *fun; JSStackFrame *fp; jsint slot; + PR_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); + fun = JS_GetPrivate(cx, obj); for (fp = cx->fp; fp; fp = fp->down) { /* Find most recent non-native function frame. */ if (fp->fun && !fp->fun->call) { - if (fp->fun->object == obj) { + if (fp->fun == fun) { slot = JSVAL_TO_INT(id); PR_ASSERT((uintN)slot < fp->fun->nvars); if ((uintN)slot < fp->nvars) @@ -382,9 +388,6 @@ js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_TRUE; } -static JSBool -Interpret(JSContext *cx, jsval *result); - /* * Find a function reference and its 'this' object implicit first parameter * under argc arguments on cx's stack, and call the function. Push missing @@ -392,92 +395,145 @@ Interpret(JSContext *cx, jsval *result); * when done. Then push the return value. */ JSBool -js_DoCall(JSContext *cx, uintN argc) +js_Invoke(JSContext *cx, uintN argc, JSBool constructing) { JSStackFrame *fp, frame; jsval *sp, *newsp; - jsval *vp, aval; - JSObject *closure, *funobj, *parent, *thisp; + jsval *vp, v; + JSObject *funobj, *parent, *thisp; + JSClass *clasp; + JSObjectOps *ops; + JSNative native; JSFunction *fun; + JSScript *script; + uintN minargs, nvars; void *mark; intN nslots, nalloc, surplus; JSBool ok; - /* Reach under our args to find the function ref on the stack. */ + /* Reach under args and this to find the callable on the stack. */ fp = cx->fp; sp = fp->sp; + + /* Once vp is set, control must flow through label out2: to return. */ vp = sp - (2 + argc); + v = *vp; - /* Reset fp->sp so error reports decompile *vp's generator. */ - fp->sp = vp; - closure = JSVAL_TO_OBJECT(*vp); - fun = JS_ValueToFunction(cx, *vp); - fp->sp = sp; - if (!fun) - return JS_FALSE; + /* A callable must be an object reference. */ + if (JSVAL_IS_PRIMITIVE(v)) + goto bad; + funobj = JSVAL_TO_OBJECT(v); - /* Get the function's parent object in case it's bound or orphaned. */ - funobj = fun->object; - parent = OBJ_GET_PARENT(funobj); + /* Load callable parent and this parameter for later. */ + parent = OBJ_GET_PARENT(cx, funobj); thisp = JSVAL_TO_OBJECT(vp[1]); - if (fun->flags & (JSFUN_BOUND_METHOD | JSFUN_GLOBAL_PARENT)) { - /* Combine the above to optimize the common case (neither bit set). */ - if (fun->flags & JSFUN_BOUND_METHOD) - thisp = parent; - else - parent = 0; + + clasp = OBJ_GET_CLASS(cx, funobj); + if (clasp != &js_FunctionClass) { + /* Function is inlined, all other classes use object ops. */ + ops = funobj->map->ops; + + /* Try converting to function, for closure and API compatibility. */ + ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); + if (!ok) + goto out2; + if (!JSVAL_IS_FUNCTION(cx, v)) { + fun = NULL; + } else { + funobj = JSVAL_TO_OBJECT(v); + fun = JS_GetPrivate(cx, funobj); + } + + /* Try a call or construct native object op, using fun as fallback. */ + native = constructing ? ops->construct : ops->call; + if (!native) { + if (fun) + goto have_fun; + goto bad; + } + + /* The callable may be a function with a script (e.g., a closure). */ + if (fun) { + script = fun->script; + minargs = fun->nargs + fun->extra; + nvars = fun->nvars; + } else { + script = NULL; + minargs = nvars = 0; + } + } else { + /* Get private data and set derived locals from it. */ + fun = JS_GetPrivate(cx, funobj); +have_fun: + native = fun->call; + script = fun->script; + minargs = fun->nargs + fun->extra; + nvars = fun->nvars; + + /* Handle bound method and dynamic scope special cases. */ + if (fun->flags) { + if (fun->flags & JSFUN_BOUND_METHOD) + thisp = parent; + else + parent = NULL; + } } - /* Make vp refer to funobj to keep it available as argv[-2]. */ - *vp = OBJECT_TO_JSVAL(funobj); - /* Initialize a stack frame, except for thisp and scopeChain. */ - frame.object = NULL; - frame.script = fun->script; + frame.callobj = frame.argsobj = NULL; + frame.script = script; frame.fun = fun; frame.argc = argc; frame.argv = sp - argc; - frame.rval = JSVAL_VOID; - frame.nvars = fun->nvars; + frame.rval = constructing ? OBJECT_TO_JSVAL(thisp) : JSVAL_VOID; + frame.nvars = nvars; frame.vars = sp; frame.down = fp; frame.annotation = NULL; frame.pc = NULL; frame.sp = sp; -#if JS_HAS_SHARP_VARS frame.sharpDepth = 0; frame.sharpArray = NULL; -#endif + frame.constructing = constructing; + frame.overrides = 0; + frame.debugging = JS_FALSE; + frame.exceptPending = JS_FALSE; -#if JS_HAS_LEXICAL_CLOSURE - /* If calling a closure, set funobj to the call object eagerly. */ - if (closure->map->clasp == &js_ClosureClass) { - frame.scopeChain = NULL; - parent = OBJ_GET_PARENT(closure); - funobj = js_GetCallObject(cx, &frame, parent); - if (!funobj) - return JS_FALSE; - thisp = parent; -#if JS_BUG_WITH_CLOSURE - while (thisp->map->clasp == &js_WithClass) { - JSObject *proto = OBJ_GET_PROTO(thisp); - if (!proto) - break; - thisp = proto; + /* Compute the 'this' parameter and store it in frame. */ + if (thisp) { + /* Some objects (e.g., With) delegate this to another object. */ + thisp = OBJ_THIS_OBJECT(cx, thisp); + if (!thisp) { + ok = JS_FALSE; + goto out2; } -#endif + } else { + /* + * ECMA requires "the global object", but in the presence of multiple + * top-level objects (windows, frames, or certain layers in the client + * object model), we prefer fun's parent. An example that causes this + * code to run: + * + * // in window w1 + * function f() { return this } + * function g() { return f } + * + * // in window w2 + * var h = w1.g() + * alert(h() == w1) + * + * The alert should display "true". + */ + thisp = parent ? parent : cx->globalObject; } -#endif - - /* Now set frame.thisp, so that the closure special case can reset it. */ frame.thisp = thisp; /* From here on, control must flow through label out: to return. */ - JS_LOCK_VOID(cx, cx->fp = &frame); + cx->fp = &frame; mark = PR_ARENA_MARK(&cx->stackPool); /* Check for missing arguments expected by the function. */ - nslots = (intN)((argc < fun->nargs) ? fun->nargs - argc : 0); + nslots = (intN)((argc < minargs) ? minargs - argc : 0); if (nslots) { /* All arguments must be contiguous, so we may have to copy actuals. */ nalloc = nslots; @@ -492,7 +548,7 @@ js_DoCall(JSContext *cx, uintN argc) /* Check whether we have enough space in the caller's frame. */ if (nalloc > 0) { /* Need space for actuals plus missing formals minus surplus. */ - newsp = AllocStack(cx, (uintN)nalloc); + newsp = js_AllocStack(cx, (uintN)nalloc, NULL); if (!newsp) { ok = JS_FALSE; goto out; @@ -521,7 +577,7 @@ js_DoCall(JSContext *cx, uintN argc) if (nslots) { surplus = (intN)((jsval *)cx->stackPool.current->avail - frame.vars); if (surplus < nslots) { - newsp = AllocStack(cx, (uintN)nslots); + newsp = js_AllocStack(cx, (uintN)nslots, NULL); if (!newsp) { ok = JS_FALSE; goto out; @@ -542,15 +598,15 @@ js_DoCall(JSContext *cx, uintN argc) SAVE_SP(&frame); /* Call the function, either a native method or an interpreted script. */ - if (fun->call) { + if (native) { frame.scopeChain = fp->scopeChain; - ok = fun->call(cx, thisp, argc, frame.argv, &frame.rval); - } else if (fun->script) { + ok = native(cx, thisp, argc, frame.argv, &frame.rval); + } else if (script) { frame.scopeChain = funobj; if (!parent) { #if JS_HAS_CALL_OBJECT /* Scope with a call object parented by cx's global object. */ - funobj = js_GetCallObject(cx, &frame, cx->globalObject); + funobj = js_GetCallObject(cx, &frame, cx->globalObject, NULL); if (!funobj) { ok = JS_FALSE; goto out; @@ -560,7 +616,7 @@ js_DoCall(JSContext *cx, uintN argc) OBJ_SET_PARENT(funobj, cx->globalObject); #endif } - ok = Interpret(cx, &aval); + ok = js_Interpret(cx, &v); #if !JS_HAS_CALL_OBJECT if (!parent) OBJ_SET_PARENT(funobj, NULL); @@ -571,11 +627,19 @@ js_DoCall(JSContext *cx, uintN argc) ok = JS_TRUE; } +#if JS_HAS_EXCEPTIONS + /* Native or script returns JS_FALSE on error or uncaught exception */ + if (!ok && frame.exceptPending) { + fp->exceptPending = JS_TRUE; + fp->exception = frame.exception; + } +#endif + out: /* - * XXX - Checking frame.annotation limits the use of the hook for - * uses other than releasing annotations, but avoids one C function - * call for every JS function call. + * Checking frame.annotation limits the use of this hook for uses other + * than releasing annotations, but avoids one C function call for every + * JS function call. */ if (frame.annotation && js_InterpreterHooks && @@ -584,39 +648,53 @@ out: } #if JS_HAS_CALL_OBJECT - /* If frame has a call object, clear its back-pointer. */ - if (frame.object) + /* If frame has a call object, sync values and clear back-pointer. */ + if (frame.callobj) ok &= js_PutCallObject(cx, &frame); #endif +#if JS_HAS_ARGS_OBJECT + /* If frame has an arguments object, sync values clear back-pointer. */ + if (frame.argsobj) + ok &= js_PutArgsObject(cx, &frame); +#endif /* Pop everything off the stack and store the return value. */ PR_ARENA_RELEASE(&cx->stackPool, mark); - JS_LOCK_VOID(cx, cx->fp = fp); - fp->sp = vp + 1; + cx->fp = fp; *vp = frame.rval; + +out2: + /* Must save sp so throw code can store an exception at sp[-1]. */ + fp->sp = vp + 1; return ok; + +bad: + js_ReportIsNotFunction(cx, vp, constructing); + ok = JS_FALSE; + goto out2; } JSBool -js_Call(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, - jsval *rval) +js_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, + uintN argc, jsval *argv, jsval *rval) { - void *mark; JSStackFrame *fp, *oldfp, frame; jsval *oldsp, *sp; + void *mark; uintN i; JSBool ok; - mark = PR_ARENA_MARK(&cx->stackPool); fp = oldfp = cx->fp; if (!fp) { memset(&frame, 0, sizeof frame); - JS_LOCK_VOID(cx, cx->fp = fp = &frame); + cx->fp = fp = &frame; } oldsp = fp->sp; - sp = AllocStack(cx, 2 + argc); - if (!sp) - return JS_FALSE; + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) { + ok = JS_FALSE; + goto out; + } fp->sp = sp; PUSH(fval); @@ -624,46 +702,60 @@ js_Call(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, for (i = 0; i < argc; i++) PUSH(argv[i]); SAVE_SP(fp); - ok = js_DoCall(cx, argc); + ok = js_Invoke(cx, argc, JS_FALSE); if (ok) { RESTORE_SP(fp); *rval = POP(); } - PR_ARENA_RELEASE(&cx->stackPool, mark); + js_FreeStack(cx, mark); +out: fp->sp = oldsp; if (oldfp != fp) - JS_LOCK_VOID(cx, cx->fp = oldfp); + cx->fp = oldfp; return ok; } JSBool -js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSStackFrame *down, - jsval *result) +js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSFunction *fun, + JSStackFrame *down, JSBool debugging, jsval *result) { JSStackFrame *oldfp, frame; JSBool ok; oldfp = cx->fp; - frame.object = NULL; + frame.callobj = frame.argsobj = NULL; frame.script = script; - frame.fun = NULL; - frame.thisp = down ? down->thisp : chain; - frame.argc = frame.nvars = 0; - frame.argv = frame.vars = NULL; + frame.fun = fun; + if (down) { + /* Propagate arg/var state for eval and the debugger API. */ + frame.thisp = down->thisp; + frame.argc = down->argc; + frame.argv = down->argv; + frame.nvars = down->nvars; + frame.vars = down->vars; + frame.annotation = down->annotation; + frame.sharpArray = down->sharpArray; + } else { + frame.thisp = chain; + frame.argc = frame.nvars = 0; + frame.argv = frame.vars = NULL; + frame.annotation = NULL; + frame.sharpArray = NULL; + } frame.rval = JSVAL_VOID; frame.down = down; - frame.annotation = NULL; frame.scopeChain = chain; frame.pc = NULL; frame.sp = oldfp ? oldfp->sp : NULL; -#if JS_HAS_SHARP_VARS frame.sharpDepth = 0; - frame.sharpArray = down ? down->sharpArray : NULL; -#endif - JS_LOCK_VOID(cx, cx->fp = &frame); - ok = Interpret(cx, result); - JS_LOCK_VOID(cx, cx->fp = oldfp); + frame.constructing = JS_FALSE; + frame.overrides = 0; + frame.debugging = debugging; + frame.exceptPending = JS_FALSE; + cx->fp = &frame; + ok = js_Interpret(cx, result); + cx->fp = oldfp; return ok; } @@ -672,35 +764,41 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSStackFrame *down, * If id is JSVAL_VOID, import all exported properties from obj. */ static JSBool -ImportProperty(JSContext *cx, JSObject *obj, jsval id) +ImportProperty(JSContext *cx, JSObject *obj, jsid id) { - JSBool all; - JSProperty *prop, *prop2; + JSBool ok; + JSIdArray *ida; + JSProperty *prop; + JSObject *obj2, *target, *funobj, *closure; JSString *str; - JSObject *source, *target, *funobj, *closure; + uintN attrs; + jsint i; JSFunction *fun; jsval value; - PR_ASSERT(JS_IS_LOCKED(cx)); - - all = JSVAL_IS_VOID(id); - if (all) { - if (!obj->map->clasp->enumerate(cx, obj)) + if (JSVAL_IS_VOID(id)) { + ida = JS_Enumerate(cx, obj); + if (!ida) return JS_FALSE; - prop = obj->map->props; - if (!prop) - return JS_TRUE; + if (ida->length == 0) + goto out; + ok = JS_TRUE; } else { - if (!js_LookupProperty(cx, obj, id, &obj, &prop)) + ida = NULL; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { - str = js_ValueToSource(cx, js_IdToValue(id)); + str = js_DecompileValueGenerator(cx, js_IdToValue(id), NULL); if (str) js_ReportIsNotDefined(cx, JS_GetStringBytes(str)); return JS_FALSE; } - if (!(prop->flags & JSPROP_EXPORTED)) { - str = js_ValueToSource(cx, js_IdToValue(id)); + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!ok) + return JS_FALSE; + if (!(attrs & JSPROP_EXPORTED)) { + str = js_DecompileValueGenerator(cx, js_IdToValue(id), NULL); if (str) { JS_ReportError(cx, "%s is not exported", JS_GetStringBytes(str)); @@ -709,29 +807,45 @@ ImportProperty(JSContext *cx, JSObject *obj, jsval id) } } - source = prop->object; target = js_FindVariableScope(cx, &fun); - if (!target) - return JS_FALSE; + if (!target) { + ok = JS_FALSE; + goto out; + } + + i = 0; do { - if (!(prop->flags & JSPROP_EXPORTED)) - continue; - value = source->slots[prop->slot]; - if (JSVAL_IS_FUNCTION(value)) { + if (ida) { + id = ida->vector[i]; + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); + if (!ok) + goto out; + if (!(attrs & JSPROP_EXPORTED)) + continue; + } + ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs); + if (!ok) + goto out; + if (JSVAL_IS_FUNCTION(cx, value)) { funobj = JSVAL_TO_OBJECT(value); closure = js_ConstructObject(cx, &js_ClosureClass, funobj, obj); - if (!closure) - return JS_FALSE; + if (!closure) { + ok = JS_FALSE; + goto out; + } value = OBJECT_TO_JSVAL(closure); } - prop2 = js_DefineProperty(cx, target, - all ? sym_id(prop->symbols) : id, - value, NULL, NULL, - prop->flags & ~JSPROP_EXPORTED); - if (!prop2) - return JS_FALSE; - } while (all && (prop = prop->next) != NULL); - return JS_TRUE; + ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL, + attrs & ~JSPROP_EXPORTED, + NULL); + if (!ok) + goto out; + } while (ida && ++i < ida->length); + +out: + if (ida) + JS_DestroyIdArray(cx, ida); + return ok; } #endif /* JS_HAS_EXPORT_IMPORT */ @@ -741,27 +855,30 @@ ImportProperty(JSContext *cx, JSObject *obj, jsval id) #define MAX_INTERP_LEVEL 30 #endif -static JSBool -Interpret(JSContext *cx, jsval *result) +JSBool +js_Interpret(JSContext *cx, jsval *result) { JSRuntime *rt; JSStackFrame *fp; JSScript *script; - jsbytecode *pc, *pc2, *endpc; + JSObject *obj, *obj2, *proto, *parent; JSBranchCallback onbranch; - JSBool ok, dropAtom, cond, valid; - void *mark; - jsval *sp, *newsp; + JSBool ok, cond; ptrdiff_t depth, len; - uintN argc, slot; + jsval *sp, *newsp; + void *mark; + jsbytecode *pc, *pc2, *endpc; JSOp op, op2; JSCodeSpec *cs; JSAtom *atom; - jsval *vp, lval, rval, ltmp, rtmp, id; - JSObject *obj, *obj2, *proto, *parent; - JSObject *withobj; - JSObject *origobj, *propobj, *iterobj; - JSProperty *prop, *prop2; + uintN argc, slot; + jsval *vp, lval, rval, ltmp, rtmp; + jsid id; + JSObject *withobj, *origobj, *propobj; + JSIdArray *ida; + jsval iter_state; + JSProperty *prop; + JSScopeProperty *sprop; JSString *str, *str2, *str3; size_t length, length2, length3; jschar *chars; @@ -781,6 +898,13 @@ Interpret(JSContext *cx, jsval *result) JSFunction *fun2; JSObject *closure; #endif +#if JS_HAS_EXPORT_IMPORT + uintN attrs; +#endif +#if JS_HAS_EXCEPTIONS + JSTryNote *tn; + ptrdiff_t offset; +#endif if (cx->interpLevel == MAX_INTERP_LEVEL) { JS_ReportError(cx, "too much recursion"); @@ -791,10 +915,11 @@ Interpret(JSContext *cx, jsval *result) rt = cx->runtime; /* - * Set registerized frame pointer and derived script pointer. + * Set registerized frame pointer and derived pointers. */ fp = cx->fp; script = fp->script; + obj = fp->scopeChain; /* * Prepare to call a user-supplied branch handler, and abort the script @@ -802,18 +927,19 @@ Interpret(JSContext *cx, jsval *result) */ onbranch = cx->branchCallback; ok = JS_TRUE; - dropAtom = JS_FALSE; #define CHECK_BRANCH(len) { \ - if (len < 0 && onbranch && !(ok = (*onbranch)(cx, script))) \ - goto out; \ + if (len < 0 && onbranch) { \ + SAVE_SP(fp); \ + if (!(ok = (*onbranch)(cx, script))) \ + goto out; \ + } \ } /* * Allocate operand and pc stack slots for the script's worst-case depth. */ - mark = PR_ARENA_MARK(&cx->stackPool); depth = (ptrdiff_t)script->depth; - newsp = AllocStack(cx, (uintN)(2 * depth)); + newsp = js_AllocStack(cx, (uintN)(2 * depth), &mark); if (!newsp) { ok = JS_FALSE; goto out; @@ -821,13 +947,6 @@ Interpret(JSContext *cx, jsval *result) newsp += depth; fp->sp = sp = newsp; -#ifdef JS_THREADSAFE - obj = NULL; - ok = JS_AddRoot(cx, &obj); - if (!ok) - goto out; -#endif - pc = script->code; endpc = pc + script->length; @@ -842,7 +961,6 @@ Interpret(JSContext *cx, jsval *result) tracefp = cx->tracefp; if (tracefp) { intN nuses, n; - jsval v; fprintf(tracefp, "%4u: ", js_PCToLineNumber(script, pc)); js_Disassemble1(cx, script, pc, pc - script->code, JS_FALSE, @@ -850,16 +968,16 @@ Interpret(JSContext *cx, jsval *result) nuses = cs->nuses; if (nuses) { fp->sp = sp - nuses; - for (n = nuses; n > 0; n--) { - v = sp[-n]; - if ((str = js_ValueToSource(cx, v)) != NULL) { + for (n = 0; n < nuses; n++) { + str = js_DecompileValueGenerator(cx, *fp->sp, NULL); + if (str != NULL) { fprintf(tracefp, "%s %s", - (n == nuses) ? " inputs:" : ",", + (n == 0) ? " inputs:" : ",", JS_GetStringBytes(str)); } + fp->sp++; } putc('\n', tracefp); - SAVE_SP(fp); } } #endif @@ -893,6 +1011,10 @@ Interpret(JSContext *cx, jsval *result) sp--; break; + case JSOP_POP2: + sp -= 2; + break; + case JSOP_POPV: *result = POP(); break; @@ -904,16 +1026,16 @@ Interpret(JSContext *cx, jsval *result) if (!withobj) goto out; fp->scopeChain = withobj; - PUSH(OBJECT_TO_JSVAL(withobj)); + PUSH_OPND(OBJECT_TO_JSVAL(withobj)); break; case JSOP_LEAVEWITH: rval = POP(); PR_ASSERT(JSVAL_IS_OBJECT(rval)); withobj = JSVAL_TO_OBJECT(rval); - PR_ASSERT(withobj->map->clasp == &js_WithClass); + PR_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); - rval = OBJ_GET_SLOT(withobj, JSSLOT_PARENT); + rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT); PR_ASSERT(JSVAL_IS_OBJECT(rval)); fp->scopeChain = JSVAL_TO_OBJECT(rval); break; @@ -929,8 +1051,7 @@ Interpret(JSContext *cx, jsval *result) break; case JSOP_IFEQ: - rval = POP(); - VALUE_TO_BOOLEAN(cx, rval, cond); + POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); @@ -938,8 +1059,7 @@ Interpret(JSContext *cx, jsval *result) break; case JSOP_IFNE: - rval = POP(); - VALUE_TO_BOOLEAN(cx, rval, cond); + POP_BOOLEAN(cx, rval, cond); if (cond != JS_FALSE) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); @@ -948,8 +1068,7 @@ Interpret(JSContext *cx, jsval *result) #if !JS_BUG_SHORT_CIRCUIT case JSOP_OR: - rval = POP(); - VALUE_TO_BOOLEAN(cx, rval, cond); + POP_BOOLEAN(cx, rval, cond); if (cond == JS_TRUE) { len = GET_JUMP_OFFSET(pc); PUSH_OPND(rval); @@ -957,8 +1076,7 @@ Interpret(JSContext *cx, jsval *result) break; case JSOP_AND: - rval = POP(); - VALUE_TO_BOOLEAN(cx, rval, cond); + POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMP_OFFSET(pc); PUSH_OPND(rval); @@ -966,56 +1084,86 @@ Interpret(JSContext *cx, jsval *result) break; #endif - case JSOP_FORNAME: - atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; + case JSOP_TOOBJECT: rval = POP(); - SAVE_SP(fp); - JS_LOCK_RUNTIME_VOID(rt, ok = js_FindVariable(cx, id, &obj, &prop)); + ok = js_ValueToObject(cx, rval, &obj); if (!ok) goto out; - PR_ASSERT(prop); - lval = OBJECT_TO_JSVAL(obj); - goto do_forinloop; + PUSH(OBJECT_TO_JSVAL(obj)); + break; - case JSOP_FORPROP: - atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; - rval = POP(); - lval = POP(); - goto do_forinloop; - -#define FIX_ELEMENT_ID(id) { \ - /* If the index is other than a nonnegative int, atomize it. */ \ - i = JSVAL_IS_INT(id) ? JSVAL_TO_INT(id) : -1; \ - if (i < 0) { \ - JS_LOCK_RUNTIME_VOID(rt, atom = js_ValueToStringAtom(cx, id)); \ +#define POP_ELEMENT_ID(id) { \ + /* If the index is not a jsint, atomize it. */ \ + id = (jsid) POP(); \ + if (JSVAL_IS_INT(id)) { \ + atom = NULL; \ + } else { \ + atom = js_ValueToStringAtom(cx, (jsval)id); \ if (!atom) { \ ok = JS_FALSE; \ goto out; \ } \ - id = (jsval)atom; \ - dropAtom = JS_TRUE; \ + id = (jsid)atom; \ } \ } + case JSOP_IN: + rval = POP(); + VALUE_TO_OBJECT(cx, rval, obj); + 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; + + case JSOP_FORNAME: + rval = POP(); + /* FALL THROUGH */ + case JSOP_FORNAME2: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + + SAVE_SP(fp); + ok = js_FindVariable(cx, id, &obj, &obj2, &prop); + if (!ok) + goto out; + PR_ASSERT(prop); + OBJ_DROP_PROPERTY(cx, obj2, prop); + lval = OBJECT_TO_JSVAL(obj); + goto do_forinloop; + + case JSOP_FORPROP: + rval = POP(); + /* FALL THROUGH */ + case JSOP_FORPROP2: + lval = POP(); + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + goto do_forinloop; + case JSOP_FORELEM: rval = POP(); - id = POP(); + /* FALL THROUGH */ + case JSOP_FORELEM2: + POP_ELEMENT_ID(id); lval = POP(); - /* If the index is other than a nonnegative int, atomize it. */ - FIX_ELEMENT_ID(id); - do_forinloop: - /* Lock the entire bytecode's critical section if threadsafe. */ - SAVE_SP(fp); - JS_LOCK_RUNTIME(rt); - ok = js_ValueToObject(cx, rval, &obj); - if (!ok) { - JS_UNLOCK_RUNTIME(rt); - goto out; + /* + * ECMA-compatible for/in bytecodes eval object just once, before loop. + */ + if (cs->format & JOF_FOR2) { + obj = JSVAL_TO_OBJECT(sp[-1]); + vp = sp - 2; + } else { + /* Old-style (pre-ECMA) JSOPs */ + SAVE_SP(fp); + ok = js_ValueToObject(cx, rval, &obj); + if (!ok) + goto out; + vp = sp - 1; } /* If the thing to the right of 'in' has no properties, break. */ @@ -1025,150 +1173,125 @@ Interpret(JSContext *cx, jsval *result) } /* - * Start from the shared scope object that owns the properties. - * This means we won't visit properties added during the loop if - * the nominal object was unmutated before the loop. But call - * enumerate first in case it mutates obj. - */ - vp = sp - 1; - if (JSVAL_IS_VOID(*vp)) { - /* Let lazy reflectors be eager so for/in finds everything. */ - ok = obj->map->clasp->enumerate(cx, obj); - if (!ok) { - JS_UNLOCK_RUNTIME(rt); - goto out; - } - } - obj = ((JSScope *)obj->map)->object; - - /* Save obj as origobj to suppress prototype properties. */ + * Save the thing to the right of 'in' as origobj. Later on we use + * this variable to suppress enumeration of shadowed prototype + * properties. + */ origobj = obj; - do_forin_again: - proto = JS_GetPrototype(cx, obj); - /* Reach under top of stack to find our property iterator. */ - if (JSVAL_IS_VOID(*vp)) { - prop = obj->map->props; + /* + * Reach under the top of stack to find our property iterator, a + * JSObject that contains the iteration state. (An object is used + * rather than a native struct so that the iteration state is + * cleaned up via GC if the for-in loop terminates abruptly.) + */ + rval = *vp; - /* Set the iterator to point to the first property. */ + /* 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) { - JS_UNLOCK_RUNTIME(rt); ok = JS_FALSE; goto out; } - OBJ_SET_SLOT(propobj, JSSLOT_PROP_OBJECT, OBJECT_TO_JSVAL(obj)); - OBJ_SET_SLOT(propobj, JSSLOT_PROP_NEXT, PRIVATE_TO_JSVAL(NULL)); - /* Rewrite the iterator so we know to do the next case. */ + /* + * 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. + */ *vp = OBJECT_TO_JSVAL(propobj); + + ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0); + 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); + propobj->slots[JSSLOT_ITR_STATE] = iter_state; + } else { - /* Use the iterator to find the next property. */ - propobj = JSVAL_TO_OBJECT(*vp); - rval = OBJ_GET_SLOT(propobj, JSSLOT_PROP_NEXT); - prop = JSVAL_TO_PRIVATE(rval); - - rval = OBJ_GET_SLOT(propobj, JSSLOT_PROP_OBJECT); - iterobj = JSVAL_TO_OBJECT(rval); - - /* If we're enumerating a prototype, reset obj and prototype. */ - if (obj != iterobj) { - obj = iterobj; - proto = JS_GetPrototype(cx, obj); - } - PR_ASSERT(!prop || prop->object == obj); + /* This is not the first iteration. Recover iterator state. */ + propobj = JSVAL_TO_OBJECT(rval); + obj = JSVAL_TO_OBJECT(propobj->slots[JSSLOT_PARENT]); + iter_state = propobj->slots[JSSLOT_ITR_STATE]; } - /* Skip deleted and predefined/hidden properties. */ - while (prop) { - if (prop->symbols && (prop->flags & JSPROP_ENUMERATE)) { - /* Have we already enumerated a clone of this property? */ - ok = js_LookupProperty(cx, origobj, - sym_id(prop->symbols), NULL, - &prop2); - if (!ok) { - JS_UNLOCK_RUNTIME(rt); - goto out; - } - if (prop2 == prop) { - /* No, go ahead and enumerate it. */ - break; - } - } - prop = prop->next; - } + enum_next_property: - if (!prop) { - /* Enumerate prototype properties, if there are any. */ - if (proto) { - obj = proto; - OBJ_SET_SLOT(propobj, JSSLOT_PROP_OBJECT, - OBJECT_TO_JSVAL(obj)); - rval = OBJ_GET_SLOT(propobj, JSSLOT_PROP_NEXT); - prop = JSVAL_TO_PRIVATE(rval); - if (prop) - js_DropProperty(cx, prop); - prop = obj->map->props; - OBJ_SET_SLOT(propobj, JSSLOT_PROP_NEXT, - PRIVATE_TO_JSVAL(prop)); - js_HoldProperty(cx, prop); - goto do_forin_again; + /* 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] = PRIVATE_TO_JSVAL(iter_state); + + /* No more jsids to iterate in obj ? */ + if (!iter_state) { + + /* Enumerate the properties on obj's prototype chain */ + obj = OBJ_GET_PROTO(cx, obj); + if (!obj) { + /* End of property list -- terminate loop. */ + rval = JSVAL_FALSE; + goto end_forinloop; } - /* End of property list -- terminate this loop. */ - rval = JSVAL_FALSE; - goto end_forinloop; - } + ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0); + if (!ok) + goto out; - /* Advance the iterator to the next property (or null). */ - rval = OBJ_GET_SLOT(propobj, JSSLOT_PROP_NEXT); - prop2 = JSVAL_TO_PRIVATE(rval); - if (prop2) - js_DropProperty(cx, prop2); - prop2 = prop->next; - OBJ_SET_SLOT(propobj, JSSLOT_PROP_NEXT, PRIVATE_TO_JSVAL(prop2)); - if (!ok) { - JS_UNLOCK_RUNTIME(rt); - goto out; - } - js_HoldProperty(cx, prop2); + /* Stash private iteration state into iterator JSObject. */ + propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); + propobj->slots[JSSLOT_ITR_STATE] = iter_state; + goto enum_next_property; + } - /* Get the int- or string-valued property name into rval. */ - rval = js_IdToValue(sym_id(prop->symbols)); + /* 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) + goto out; + if (prop) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + + /* Yes, don't enumerate again. Go to the next property */ + if (obj2 != obj) + goto enum_next_property; + } + } /* Convert lval to a non-null object containing id. */ VALUE_TO_OBJECT(cx, lval, obj); /* Make sure rval is a string for uniformity and compatibility. */ - if (cx->version < JSVERSION_1_2 && JSVAL_IS_INT(rval)) { + if (!JSVAL_IS_INT(rval)) { + rval = ATOM_KEY((JSAtom *)rval); + } else if (cx->version < JSVERSION_1_2) { str = js_NumberToString(cx, (jsdouble) JSVAL_TO_INT(rval)); if (!str) { - JS_UNLOCK_RUNTIME(rt); ok = JS_FALSE; goto out; } - /* Hold via sp[0] in case the GC runs under js_SetProperty. */ + /* Hold via sp[0] in case the GC runs under OBJ_SET_PROPERTY. */ rval = sp[0] = STRING_TO_JSVAL(str); } /* Set the variable obj[id] to refer to rval. */ - prop = js_SetProperty(cx, obj, id, &rval); - if (!prop) { - JS_UNLOCK_RUNTIME(rt); - ok = JS_FALSE; + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + if (!ok) goto out; - } /* Push true to keep looping through properties. */ rval = JSVAL_TRUE; end_forinloop: - if (dropAtom) { - js_DropAtom(cx, atom); - dropAtom = JS_FALSE; - } - JS_UNLOCK_RUNTIME(rt); PUSH_OPND(rval); break; @@ -1186,64 +1309,57 @@ Interpret(JSContext *cx, jsval *result) PUSH_OPND(rval); break; -#define PROPERTY_OP(call, result) { \ +#define PROPERTY_OP(call) { \ /* Pop the left part and resolve it to a non-null object. */ \ lval = POP(); \ VALUE_TO_OBJECT(cx, lval, obj); \ \ - /* Get or set the property, set result zero if error, non-zero if ok. */ \ - JS_LOCK_RUNTIME(rt); \ + /* Get or set the property, set ok false if error, true if success. */ \ call; \ - JS_UNLOCK_RUNTIME(rt); \ - if (!result) { \ - ok = JS_FALSE; \ + if (!ok) \ goto out; \ - } \ } -#define ELEMENT_OP(call, result) { \ - id = POP(); \ - FIX_ELEMENT_ID(id); \ - PROPERTY_OP(call, result); \ - if (dropAtom) { \ - JS_LOCK_RUNTIME_VOID(rt, js_DropAtom(cx, atom)); \ - dropAtom = JS_FALSE; \ - } \ +#define ELEMENT_OP(call) { \ + POP_ELEMENT_ID(id); \ + PROPERTY_OP(call); \ } #define CACHED_GET(call) { \ PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop); \ if (PROP_FOUND(prop)) { \ - obj2 = prop->object; \ - slot = (uintN)prop->slot; \ - rval = obj2->slots[slot]; \ - ok = prop->getter(cx, obj, prop->id, &rval); \ + sprop = (JSScopeProperty *)prop; \ + slot = (uintN)sprop->slot; \ + rval = OBJ_GET_SLOT(cx, obj, slot); \ + ok = SPROP_GET(cx, sprop, obj, obj, &rval); \ if (ok) \ - obj2->slots[slot] = rval; \ - else \ - prop = NULL; \ + OBJ_SET_SLOT(cx, obj, slot, rval); \ } else { \ SAVE_SP(fp); \ - prop = call; \ - if (prop) \ - PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop); \ + ok = call; \ + /* No fill here: js_GetProperty fills the cache. */ \ } \ } +#if JS_BUG_SET_ENUMERATE +#define SET_ENUMERATE_ATTR(sprop) ((sprop)->attrs |= JSPROP_ENUMERATE) +#else +#define SET_ENUMERATE_ATTR(sprop) ((void)0) +#endif + #define CACHED_SET(call) { \ PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop); \ if (PROP_FOUND(prop) && \ - !(prop->flags & (JSPROP_READONLY | JSPROP_ASSIGNHACK))) { \ - ok = prop->setter(cx, obj, prop->id, &rval); \ + !(sprop = (JSScopeProperty *)prop, \ + sprop->attrs & (JSPROP_READONLY | JSPROP_ASSIGNHACK))) { \ + ok = SPROP_SET(cx, sprop, obj, obj, &rval); \ if (ok) { \ - prop->flags |= JSPROP_ENUMERATE; \ - prop->object->slots[prop->slot] = rval; \ - } else { \ - prop = NULL; \ + SET_ENUMERATE_ATTR(sprop); \ + OBJ_SET_SLOT(cx, obj, sprop->slot, rval); \ } \ } else { \ SAVE_SP(fp); \ - prop = call; \ + ok = call; \ /* No fill here: js_SetProperty writes through the cache. */ \ } \ } @@ -1251,66 +1367,64 @@ Interpret(JSContext *cx, jsval *result) case JSOP_SETNAME: /* Get an immediate atom naming the variable to set. */ atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; + id = (jsid)atom; SAVE_SP(fp); - JS_LOCK_RUNTIME(rt); - ok = js_FindVariable(cx, id, &obj, &prop); - if (!ok) { - JS_UNLOCK_RUNTIME(rt); + ok = js_FindVariable(cx, id, &obj, &obj2, &prop); + if (!ok) goto out; - } + PR_ASSERT(prop); + + OBJ_DROP_PROPERTY(cx, obj2, prop); /* Pop the right-hand side and set obj[id] to it. */ rval = POP(); /* Try to hit the property cache, FindVariable primes it. */ - CACHED_SET(js_SetProperty(cx, obj, id, &rval)); - if (!prop) { - JS_UNLOCK_RUNTIME(rt); - ok = JS_FALSE; + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + if (!ok) goto out; - } /* Push the right-hand side as our result. */ PUSH_OPND(rval); - - /* Quicken this bytecode if possible. */ - if (obj == fp->scopeChain && - prop && (prop->flags & JSPROP_PERMANENT)) { - if (prop->setter == js_SetArgument) { - js_PatchOpcode(cx, script, pc, JSOP_SETARG); - slot = (uintN)JSVAL_TO_INT(prop->id); - SET_ARGNO(pc, slot); - } else if (prop->setter == js_SetLocalVariable) { - js_PatchOpcode(cx, script, pc, JSOP_SETVAR); - slot = (uintN)JSVAL_TO_INT(prop->id); - SET_VARNO(pc, slot); - } - } - - JS_UNLOCK_RUNTIME(rt); break; -#define INTEGER_OP(OP, EXTRA_CODE, LEFT_CAST) { \ - valid = JS_TRUE; \ + case JSOP_BINDNAME: + atom = GET_ATOM(cx, script, pc); + SAVE_SP(fp); + ok = js_FindVariable(cx, (jsid)atom, &obj, &obj2, &prop); + if (!ok) + goto out; + OBJ_DROP_PROPERTY(cx, obj2, prop); + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_SETNAME2: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + rval = POP(); + lval = POP(); + PR_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); + obj = JSVAL_TO_OBJECT(lval); + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + +#define INTEGER_OP(OP, EXTRA_CODE) { \ SAVE_SP(fp); \ - ok = PopInt(cx, &j, &valid) && PopInt(cx, &i, &valid); \ + ok = PopInt(cx, &j) && PopInt(cx, &i); \ RESTORE_SP(fp); \ if (!ok) \ goto out; \ EXTRA_CODE \ - if (!valid) { \ - PUSH_OPND(DOUBLE_TO_JSVAL(rt->jsNaN)); \ - } else { \ - i = LEFT_CAST i OP j; \ - PUSH_NUMBER(cx, i); \ - } \ + i = i OP j; \ + PUSH_NUMBER(cx, i); \ } -#define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;, (jsint)) -#define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;, (jsint)) -#define UNSIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;, (jsuint)) +#define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;) +#define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;) case JSOP_BITOR: BITWISE_OP(|); @@ -1343,21 +1457,24 @@ Interpret(JSContext *cx, jsval *result) if (ltmp && rtmp) { \ cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \ } else { \ - d = ltmp ? *rt->jsNaN : JSVAL_TO_INT(lval); \ - d2 = rtmp ? *rt->jsNaN : JSVAL_TO_INT(rval); \ + d = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN; \ + d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN; \ cond = COMPARE_DOUBLES(d, OP, d2, JS_FALSE); \ } \ } else { \ - ltmp = lval, rtmp = rval; \ - VALUE_TO_PRIMITIVE(cx, lval, &lval); \ - VALUE_TO_PRIMITIVE(cx, rval, &rval); \ + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval); \ + /* Need lval for strcmp or ToNumber later, so root it via sp[0]. */ \ + sp[0] = lval; \ + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval); \ if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ str = JSVAL_TO_STRING(lval); \ str2 = JSVAL_TO_STRING(rval); \ cond = js_CompareStrings(str, str2) OP 0; \ } else { \ - VALUE_TO_NUMBER(cx, ltmp, d); \ - VALUE_TO_NUMBER(cx, rtmp, d2); \ + /* Need rval after ToNumber(lval), so root it via sp[1]. */ \ + sp[1] = rval; \ + VALUE_TO_NUMBER(cx, lval, d); \ + VALUE_TO_NUMBER(cx, rval, d2); \ cond = COMPARE_DOUBLES(d, OP, d2, JS_FALSE); \ } \ } \ @@ -1388,16 +1505,22 @@ Interpret(JSContext *cx, jsval *result) } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \ cond = 1 OP 0; \ } else { \ - ltmp = lval, rtmp = rval; \ - VALUE_TO_PRIMITIVE(cx, lval, &lval); \ - VALUE_TO_PRIMITIVE(cx, rval, &rval); \ - if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ + if (ltmp == JSVAL_OBJECT) { \ + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &lval); \ + ltmp = JSVAL_TAG(lval); \ + } else if (rtmp == JSVAL_OBJECT) { \ + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rval); \ + rtmp = JSVAL_TAG(rval); \ + } \ + if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ str = JSVAL_TO_STRING(lval); \ str2 = JSVAL_TO_STRING(rval); \ cond = js_CompareStrings(str, str2) OP 0; \ } else { \ - VALUE_TO_NUMBER(cx, ltmp, d); \ - VALUE_TO_NUMBER(cx, rtmp, d2); \ + /* Need rval after ToNumber(lval), so root it via sp[1]. */ \ + sp[1] = rval; \ + VALUE_TO_NUMBER(cx, lval, d); \ + VALUE_TO_NUMBER(cx, rval, d2); \ cond = COMPARE_DOUBLES(d, OP, d2, IFNAN); \ } \ } \ @@ -1484,34 +1607,50 @@ Interpret(JSContext *cx, jsval *result) break; case JSOP_URSH: - UNSIGNED_SHIFT_OP(>>); + { + uint32 u; + + SAVE_SP(fp); + ok = PopInt(cx, &j) && PopUint(cx, &u); + RESTORE_SP(fp); + if (!ok) + goto out; + j &= 31; + d = u >> j; + PUSH_NUMBER(cx, d); break; + } #undef INTEGER_OP #undef BITWISE_OP #undef SIGNED_SHIFT_OP -#undef UNSIGNED_SHIFT_OP case JSOP_ADD: rval = rtmp = POP(); lval = ltmp = POP(); - VALUE_TO_PRIMITIVE(cx, lval, &lval); - VALUE_TO_PRIMITIVE(cx, rval, &rval); - if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &lval); + if ((cond = JSVAL_IS_STRING(lval)) != 0) { + /* + * Keep lval on the stack so it isn't GC'd during either the + * next VALUE_TO_PRIMITIVE or the js_ValueToString(cx, rval). + */ + sp[0] = lval; + } + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rval); + if (cond || JSVAL_IS_STRING(rval)) { if (cond) { - /* Keep a ref to str on the stack so it isn't GC'd. */ str = JSVAL_TO_STRING(lval); - PUSH(STRING_TO_JSVAL(str)); SAVE_SP(fp); - ok = (str2 = JS_ValueToString(cx, rval)) != 0; - (void) POP(); + ok = (str2 = js_ValueToString(cx, rval)) != NULL; } else { - /* Keep a ref to str2 on the stack so it isn't GC'd. */ + /* + * Keep rval on the stack so it isn't GC'd during the next + * js_ValueToString. + */ + sp[1] = rval; str2 = JSVAL_TO_STRING(rval); - PUSH(STRING_TO_JSVAL(str2)); SAVE_SP(fp); - ok = (str = JS_ValueToString(cx, lval)) != 0; - (void) POP(); + ok = (str = js_ValueToString(cx, lval)) != NULL; } if (!ok) goto out; @@ -1564,6 +1703,12 @@ Interpret(JSContext *cx, jsval *result) POP_NUMBER(cx, d2); POP_NUMBER(cx, d); if (d2 == 0) { +#ifdef XP_PC + /* XXX MSVC miscompiles such that (NaN == 0) */ + if (JSDOUBLE_IS_NaN(d2)) + rval = DOUBLE_TO_JSVAL(rt->jsNaN); + else +#endif if (d == 0 || JSDOUBLE_IS_NaN(d)) rval = DOUBLE_TO_JSVAL(rt->jsNaN); else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) @@ -1593,24 +1738,18 @@ Interpret(JSContext *cx, jsval *result) break; case JSOP_NOT: - rval = POP(); - VALUE_TO_BOOLEAN(cx, rval, cond); + POP_BOOLEAN(cx, rval, cond); PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); break; case JSOP_BITNOT: - valid = JS_TRUE; SAVE_SP(fp); - ok = PopInt(cx, &i, &valid); + ok = PopInt(cx, &i); RESTORE_SP(fp); if (!ok) goto out; - if (!valid) { - PUSH_OPND(DOUBLE_TO_JSVAL(rt->jsNaN)); - } else { - i = ~i; - PUSH_NUMBER(cx, i); - } + i = ~i; + PUSH_NUMBER(cx, i); break; case JSOP_NEG: @@ -1632,34 +1771,37 @@ Interpret(JSContext *cx, jsval *result) vp = sp - (2 + argc); PR_ASSERT(vp >= newsp); - /* Reset fp->sp so error reports decompile *vp's generator. */ - fp->sp = vp; - JS_LOCK_RUNTIME(rt); - fun = js_ValueToFunction(cx, *vp); - if (!fun) { - JS_UNLOCK_RUNTIME(rt); - ok = JS_FALSE; - goto out; + lval = *vp; + if (!JSVAL_IS_OBJECT(lval) || + (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || + !obj2->map->ops->construct) { + fun = js_ValueToFunction(cx, vp, JS_TRUE); + if (!fun) { + ok = JS_FALSE; + goto out; + } + obj2 = fun->object; } - /* Get the constructor prototype object for this function. */ - prop = js_GetProperty(cx, fun->object, - (jsval)rt->atomState.classPrototypeAtom, - &rval); - if (!prop) { - JS_UNLOCK_RUNTIME(rt); - ok = JS_FALSE; - goto out; + if (!obj2) { + proto = parent = NULL; + fun = NULL; + } else { + /* Get the constructor prototype object for this function. */ + ok = OBJ_GET_PROPERTY(cx, obj2, + (jsid)rt->atomState.classPrototypeAtom, + &rval); + if (!ok) + goto out; + proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; + parent = OBJ_GET_PARENT(cx, obj2); } - proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; - parent = OBJ_GET_PARENT(fun->object); /* If there is no class prototype, use js_ObjectClass. */ if (!proto) obj = js_NewObject(cx, &js_ObjectClass, NULL, parent); else - obj = js_NewObject(cx, proto->map->clasp, proto, parent); - JS_UNLOCK_RUNTIME(rt); + obj = js_NewObject(cx, OBJ_GET_CLASS(cx, proto), proto, parent); if (!obj) { ok = JS_FALSE; goto out; @@ -1668,51 +1810,71 @@ Interpret(JSContext *cx, jsval *result) /* Now we have an object with a constructor method; call it. */ vp[1] = OBJECT_TO_JSVAL(obj); SAVE_SP(fp); - ok = js_DoCall(cx, argc); + ok = js_Invoke(cx, argc, JS_TRUE); RESTORE_SP(fp); if (!ok) { cx->newborn[GCX_OBJECT] = NULL; +#if JS_HAS_EXCEPTIONS + if (fp->exceptPending) { + /* throw expects to have the exception on the stack */ + sp[-1] = fp->exception; + goto throw; + } +#endif goto out; } - /* Check the return value and see if it does not refer to obj. */ + /* Check the return value and update obj from it. */ rval = *vp; - if (!JSVAL_IS_OBJECT(rval)) { - *vp = OBJECT_TO_JSVAL(obj); - } else { - obj2 = JSVAL_TO_OBJECT(rval); - if (obj2 != obj) - obj = obj2; + if (JSVAL_IS_PRIMITIVE(rval)) { + if (fun || !JSVERSION_IS_ECMA(cx->version)) { + /* Pre-ECMA code would coerce result to obj. */ + *vp = OBJECT_TO_JSVAL(obj); + break; + } + str = js_ValueToString(cx, rval); + if (str) { + JS_ReportError(cx, "invalid new expression result %s", + JS_GetStringBytes(str)); + } + ok = JS_FALSE; + goto out; } + obj = JSVAL_TO_OBJECT(rval); break; case JSOP_DELNAME: atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; + id = (jsid)atom; - JS_LOCK_RUNTIME(rt); - ok = js_FindProperty(cx, id, &obj, &prop); - if (ok && prop) { - /* Call js_DeleteProperty2 to avoid id re-lookup. */ - ok = js_DeleteProperty2(cx, obj, prop, id, &rval); - } - JS_UNLOCK_RUNTIME(rt); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; + + /* ECMA says to return true if name is undefined or inherited. */ + rval = JSVAL_TRUE; + if (prop) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (obj2 == obj) { + ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + } + } PUSH_OPND(rval); break; case JSOP_DELPROP: atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; + id = (jsid)atom; SAVE_SP(fp); - PROPERTY_OP(ok = js_DeleteProperty(cx, obj, id, &rval), ok); + PROPERTY_OP(ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); PUSH_OPND(rval); break; case JSOP_DELELEM: SAVE_SP(fp); - ELEMENT_OP(ok = js_DeleteProperty(cx, obj, id, &rval), ok); + ELEMENT_OP(ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); PUSH_OPND(rval); break; @@ -1734,39 +1896,19 @@ Interpret(JSContext *cx, jsval *result) case JSOP_NAMEINC: case JSOP_NAMEDEC: atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; + id = (jsid)atom; SAVE_SP(fp); - JS_LOCK_RUNTIME_VOID(rt, ok = js_FindVariable(cx, id, &obj, &prop)); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; - - /* Quicken this bytecode if possible. */ - if (obj == fp->scopeChain && (prop->flags & JSPROP_PERMANENT)) { - if (prop->setter == js_SetArgument) { - js_PatchOpcode(cx, script, pc, - (cs->format & JOF_POST) - ? (cs->format & JOF_INC) - ? JSOP_ARGINC : JSOP_ARGDEC - : (cs->format & JOF_INC) - ? JSOP_INCARG : JSOP_DECARG); - slot = (uintN)JSVAL_TO_INT(prop->id); - SET_ARGNO(pc, slot); - goto do_argincop; - } - if (prop->setter == js_SetLocalVariable) { - js_PatchOpcode(cx, script, pc, - (cs->format & JOF_POST) - ? (cs->format & JOF_INC) - ? JSOP_VARINC : JSOP_VARDEC - : (cs->format & JOF_INC) - ? JSOP_INCVAR : JSOP_DECVAR); - slot = (uintN)JSVAL_TO_INT(prop->id); - SET_VARNO(pc, slot); - goto do_varincop; - } + if (!prop) { + js_ReportIsNotDefined(cx, ATOM_BYTES(atom)); + ok = JS_FALSE; + goto out; } + OBJ_DROP_PROPERTY(cx, obj2, prop); lval = OBJECT_TO_JSVAL(obj); goto do_incop; @@ -1775,7 +1917,7 @@ Interpret(JSContext *cx, jsval *result) case JSOP_PROPINC: case JSOP_PROPDEC: atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; + id = (jsid)atom; lval = POP(); goto do_incop; @@ -1783,21 +1925,16 @@ Interpret(JSContext *cx, jsval *result) case JSOP_DECELEM: case JSOP_ELEMINC: case JSOP_ELEMDEC: - id = POP(); + POP_ELEMENT_ID(id); lval = POP(); - /* If the index is other than a nonnegative int, atomize it. */ - FIX_ELEMENT_ID(id); - do_incop: VALUE_TO_OBJECT(cx, lval, obj); /* The operand must contain a number. */ - CACHED_GET(js_GetProperty(cx, obj, id, &rval)); - if (!prop) { - ok = JS_FALSE; + CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); + if (!ok) goto out; - } VALUE_TO_NUMBER(cx, rval, d); /* Compute the post- or pre-incremented value. */ @@ -1810,15 +1947,9 @@ Interpret(JSContext *cx, jsval *result) ok = js_NewNumberValue(cx, d, &rval); if (!ok) goto out; - CACHED_SET(js_SetProperty(cx, obj, id, &rval)); - if (!prop) { - ok = JS_FALSE; + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + if (!ok) goto out; - } - if (dropAtom) { - JS_LOCK_RUNTIME_VOID(rt, js_DropAtom(cx, atom)); - dropAtom = JS_FALSE; - } PUSH_NUMBER(cx, d2); break; @@ -1826,7 +1957,6 @@ Interpret(JSContext *cx, jsval *result) case JSOP_DECARG: case JSOP_ARGINC: case JSOP_ARGDEC: - do_argincop: slot = (uintN)GET_ARGNO(pc); PR_ASSERT(slot < fp->fun->nargs); rval = fp->argv[slot]; @@ -1849,7 +1979,6 @@ Interpret(JSContext *cx, jsval *result) case JSOP_DECVAR: case JSOP_VARINC: case JSOP_VARDEC: - do_varincop: slot = (uintN)GET_VARNO(pc); PR_ASSERT(slot < fp->fun->nvars); rval = fp->vars[slot]; @@ -1871,34 +2000,30 @@ Interpret(JSContext *cx, jsval *result) case JSOP_GETPROP: /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; - PROPERTY_OP(CACHED_GET(js_GetProperty(cx, obj, id, &rval)), - prop); + id = (jsid)atom; + PROPERTY_OP(CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); PUSH_OPND(rval); break; case JSOP_SETPROP: - /* Pop the right-hand side into rval for js_SetProperty. */ + /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */ rval = POP(); /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; - PROPERTY_OP(CACHED_SET(js_SetProperty(cx, obj, id, &rval)), - prop); + id = (jsid)atom; + PROPERTY_OP(CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); PUSH_OPND(rval); break; case JSOP_GETELEM: - ELEMENT_OP(CACHED_GET(js_GetProperty(cx, obj, id, &rval)), - prop); + ELEMENT_OP(CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); PUSH_OPND(rval); break; case JSOP_SETELEM: rval = POP(); - ELEMENT_OP(CACHED_SET(js_SetProperty(cx, obj, id, &rval)), - prop); + ELEMENT_OP(CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); PUSH_OPND(rval); break; @@ -1909,65 +2034,68 @@ Interpret(JSContext *cx, jsval *result) case JSOP_CALL: argc = GET_ARGC(pc); SAVE_SP(fp); - ok = js_DoCall(cx, argc); + ok = js_Invoke(cx, argc, JS_FALSE); RESTORE_SP(fp); - if (!ok) + if (!ok) { +#if JS_HAS_EXCEPTIONS + if (fp->exceptPending) { + /* throw expects to have the exception on the stack */ + sp[-1] = fp->exception; + goto throw; + } +#endif goto out; + } + obj = NULL; break; case JSOP_NAME: atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; + id = (jsid)atom; - JS_LOCK_RUNTIME(rt); - ok = js_FindProperty(cx, id, &obj, &prop); - if (!ok) { - JS_UNLOCK_RUNTIME(rt); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + if (!ok) goto out; - } if (!prop) { /* Kludge to allow (typeof foo == "undefined") tests. */ for (pc2 = pc + len; pc2 < endpc; pc2++) { op2 = (JSOp)*pc2; if (op2 == JSOP_TYPEOF) { - JS_UNLOCK_RUNTIME(rt); PUSH_OPND(JSVAL_VOID); goto advance_pc; } if (op2 != JSOP_NOP) break; } - JS_UNLOCK_RUNTIME(rt); js_ReportIsNotDefined(cx, ATOM_BYTES(atom)); ok = JS_FALSE; goto out; } + /* Take the slow path if prop was not found in a native object. */ + if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + } + /* Get and push the obj[id] property's value. */ - obj2 = prop->object; - slot = (uintN)prop->slot; - rval = obj2->slots[slot]; - ok = prop->getter(cx, obj, prop->id, &rval); + sprop = (JSScopeProperty *)prop; + slot = (uintN)sprop->slot; + rval = LOCKED_OBJ_GET_SLOT(obj2, slot); + JS_UNLOCK_OBJ(cx, obj2); + ok = SPROP_GET(cx, sprop, obj, obj2, &rval); + JS_LOCK_OBJ(cx, obj2); if (!ok) { - JS_UNLOCK_RUNTIME(rt); + OBJ_DROP_PROPERTY(cx, obj2, prop); goto out; } - obj2->slots[slot] = rval; + LOCKED_OBJ_SET_SLOT(obj2, slot, rval); + OBJ_DROP_PROPERTY(cx, obj2, prop); PUSH_OPND(rval); - - /* Quicken this bytecode if possible. */ - if (obj == fp->scopeChain && (prop->flags & JSPROP_PERMANENT)) { - if (prop->getter == js_GetArgument) { - js_PatchOpcode(cx, script, pc, JSOP_GETARG); - slot = (uintN)JSVAL_TO_INT(prop->id); - SET_ARGNO(pc, slot); - } else if (prop->getter == js_GetLocalVariable) { - js_PatchOpcode(cx, script, pc, JSOP_GETVAR); - slot = (uintN)JSVAL_TO_INT(prop->id); - SET_VARNO(pc, slot); - } - } - JS_UNLOCK_RUNTIME(rt); break; case JSOP_UINT16: @@ -1986,7 +2114,6 @@ Interpret(JSContext *cx, jsval *result) atom = GET_ATOM(cx, script, pc); rval = ATOM_KEY(atom); PR_ASSERT(JSVAL_IS_OBJECT(rval)); - obj = NULL; PUSH_OPND(rval); break; @@ -1999,13 +2126,11 @@ Interpret(JSContext *cx, jsval *result) break; case JSOP_NULL: - obj = NULL; PUSH_OPND(JSVAL_NULL); break; case JSOP_THIS: - obj = fp->thisp; - PUSH_OPND(OBJECT_TO_JSVAL(obj)); + PUSH_OPND(OBJECT_TO_JSVAL(fp->thisp)); break; case JSOP_FALSE: @@ -2018,17 +2143,14 @@ Interpret(JSContext *cx, jsval *result) #if JS_HAS_SWITCH_STATEMENT case JSOP_TABLESWITCH: - valid = JS_TRUE; SAVE_SP(fp); - ok = PopInt(cx, &i, &valid); + ok = PopInt(cx, &i); RESTORE_SP(fp); if (!ok) goto out; pc2 = pc; len = GET_JUMP_OFFSET(pc2); - if (!valid) - goto advance_pc; pc2 += 2; low = GET_JUMP_OFFSET(pc2); @@ -2100,7 +2222,7 @@ Interpret(JSContext *cx, jsval *result) * If the nearest variable scope is a function, not a call object, * replace it in the scope chain with its call object. */ - JS_LOCK_RUNTIME_VOID(rt, obj = js_FindVariableScope(cx, &fun)); + obj = js_FindVariableScope(cx, &fun); if (!obj) { ok = JS_FALSE; goto out; @@ -2112,10 +2234,10 @@ Interpret(JSContext *cx, jsval *result) */ if (fp->scopeChain != obj) { obj = fp->scopeChain; - PR_ASSERT(obj->map->clasp == &js_WithClass); + PR_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_WithClass); #if JS_BUG_WITH_CLOSURE - while (obj->map->clasp == &js_WithClass) { - proto = OBJ_GET_PROTO(obj); + while (OBJ_GET_CLASS(cx, obj) == &js_WithClass) { + proto = OBJ_GET_PROTO(cx, obj); if (!proto) break; obj = proto; @@ -2151,74 +2273,90 @@ Interpret(JSContext *cx, jsval *result) * but only if fun2 is not anonymous. */ if (fun2->atom) { - JS_LOCK_RUNTIME_VOID(rt, - prop = js_DefineProperty(cx, obj, (jsval)fun2->atom, - OBJECT_TO_JSVAL(closure), - NULL, NULL, JSPROP_ENUMERATE)); - if (!prop) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)fun2->atom, + OBJECT_TO_JSVAL(closure), + NULL, NULL, JSPROP_ENUMERATE, + NULL); + if (!ok) { cx->newborn[GCX_OBJECT] = NULL; - ok = JS_FALSE; goto out; } } - PUSH(OBJECT_TO_JSVAL(closure)); + PUSH_OPND(OBJECT_TO_JSVAL(closure)); break; #endif /* JS_HAS_LEXICAL_CLOSURE */ #if JS_HAS_EXPORT_IMPORT case JSOP_EXPORTALL: - JS_LOCK_RUNTIME(rt); obj = js_FindVariableScope(cx, &fun); if (!obj) { ok = JS_FALSE; } else { - ok = obj->map->clasp->enumerate(cx, obj); - if (ok) { - for (prop = obj->map->props; prop; prop = prop->next) { - if (prop->flags & JSPROP_ENUMERATE) { - ok = prop->getter(cx, obj, id, &rval); - if (!ok) - break; - prop->flags |= JSPROP_EXPORTED; + ida = JS_Enumerate(cx, obj); + if (!ida) { + ok = JS_FALSE; + } else { + for (i = 0, j = ida->length; i < j; i++) { + id = ida->vector[i]; + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + break; + if (!prop) + continue; + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); } + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!ok) + break; } + JS_DestroyIdArray(cx, ida); } } - JS_UNLOCK_RUNTIME(rt); break; case JSOP_EXPORTNAME: atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; - JS_LOCK_RUNTIME(rt); + id = (jsid)atom; obj = js_FindVariableScope(cx, &fun); - prop = obj ? js_GetProperty(cx, obj, id, &rval) : NULL; - if (!prop) { - JS_UNLOCK_RUNTIME(rt); + if (!obj) { ok = JS_FALSE; goto out; } - prop->flags |= JSPROP_EXPORTED; - JS_UNLOCK_RUNTIME(rt); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + goto out; + if (!prop) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + JSPROP_EXPORTED, NULL); + } else { + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + if (!ok) + goto out; break; case JSOP_IMPORTALL: - id = JSVAL_VOID; - PROPERTY_OP(ok = ImportProperty(cx, obj, id), - ok); + id = (jsid)JSVAL_VOID; + PROPERTY_OP(ok = ImportProperty(cx, obj, id)); break; case JSOP_IMPORTPROP: /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; - PROPERTY_OP(ok = ImportProperty(cx, obj, id), - ok); + id = (jsid)atom; + PROPERTY_OP(ok = ImportProperty(cx, obj, id)); break; case JSOP_IMPORTELEM: - ELEMENT_OP(ok = ImportProperty(cx, obj, id), - ok); + ELEMENT_OP(ok = ImportProperty(cx, obj, id)); break; #endif /* JS_HAS_EXPORT_IMPORT */ @@ -2240,14 +2378,14 @@ Interpret(JSContext *cx, jsval *result) break; case JSOP_GETARG: - obj = fp->scopeChain; + obj = NULL; slot = (uintN)GET_ARGNO(pc); PR_ASSERT(slot < fp->fun->nargs); PUSH_OPND(fp->argv[slot]); break; case JSOP_SETARG: - obj = fp->scopeChain; + obj = NULL; slot = (uintN)GET_ARGNO(pc); PR_ASSERT(slot < fp->fun->nargs); vp = &fp->argv[slot]; @@ -2256,14 +2394,14 @@ Interpret(JSContext *cx, jsval *result) break; case JSOP_GETVAR: - obj = fp->scopeChain; + obj = NULL; slot = (uintN)GET_VARNO(pc); PR_ASSERT(slot < fp->fun->nvars); PUSH_OPND(fp->vars[slot]); break; case JSOP_SETVAR: - obj = fp->scopeChain; + obj = NULL; slot = (uintN)GET_VARNO(pc); PR_ASSERT(slot < fp->fun->nvars); vp = &fp->vars[slot]; @@ -2271,19 +2409,15 @@ Interpret(JSContext *cx, jsval *result) *vp = sp[-1]; break; -#ifdef JS_HAS_OBJECT_LITERAL +#ifdef JS_HAS_INITIALIZERS case JSOP_NEWINIT: argc = 0; -#if JS_HAS_SHARP_VARS fp->sharpDepth++; -#endif goto do_new; case JSOP_ENDINIT: -#if JS_HAS_SHARP_VARS if (--fp->sharpDepth == 0) fp->sharpArray = NULL; -#endif break; case JSOP_INITPROP: @@ -2293,7 +2427,7 @@ Interpret(JSContext *cx, jsval *result) /* Get the immediate property name into id. */ atom = GET_ATOM(cx, script, pc); - id = (jsval)atom; + id = (jsid)atom; goto do_init; case JSOP_INITELEM: @@ -2302,8 +2436,7 @@ Interpret(JSContext *cx, jsval *result) rval = POP(); /* Pop and conditionally atomize the element id. */ - id = POP(); - FIX_ELEMENT_ID(id); + POP_ELEMENT_ID(id); do_init: /* Find the object being initialized at top of stack. */ @@ -2312,15 +2445,9 @@ Interpret(JSContext *cx, jsval *result) obj = JSVAL_TO_OBJECT(lval); /* Set the property named by obj[id] to rval. */ - JS_LOCK_RUNTIME_VOID(rt, prop = js_SetProperty(cx, obj, id, &rval)); - if (!prop) { - ok = JS_FALSE; + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + if (!ok) goto out; - } - if (dropAtom) { - JS_LOCK_RUNTIME_VOID(rt, js_DropAtom(cx, atom)); - dropAtom = JS_FALSE; - } break; #if JS_HAS_SHARP_VARS @@ -2335,39 +2462,96 @@ Interpret(JSContext *cx, jsval *result) fp->sharpArray = obj; } i = (jsint) GET_ATOM_INDEX(pc); - id = INT_TO_JSVAL(i); + id = (jsid) INT_TO_JSVAL(i); rval = sp[-1]; - PR_ASSERT(JSVAL_IS_OBJECT(rval)); - JS_LOCK_RUNTIME_VOID(rt, prop = js_SetProperty(cx, obj, id, &rval)); - if (!prop) { - ok = JS_FALSE; - goto out; + if (JSVAL_IS_PRIMITIVE(rval)) { + JS_ReportError(cx, "invalid sharp variable definition #%u=", + (unsigned) i); + ok = JS_FALSE; + goto out; } + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; break; case JSOP_USESHARP: i = (jsint) GET_ATOM_INDEX(pc); - id = INT_TO_JSVAL(i); + id = (jsid) INT_TO_JSVAL(i); obj = fp->sharpArray; if (!obj) { rval = JSVAL_VOID; } else { - JS_LOCK_RUNTIME_VOID(rt, - prop = js_GetProperty(cx, obj, id, &rval)); - if (!prop) { - ok = JS_FALSE; + ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); + if (!ok) goto out; - } } if (!JSVAL_IS_OBJECT(rval)) { - JS_ReportError(cx, "invalid sharp variable #%u#", (unsigned) i); + JS_ReportError(cx, "invalid sharp variable use #%u#", + (unsigned) i); ok = JS_FALSE; goto out; } PUSH_OPND(rval); break; #endif /* JS_HAS_SHARP_VARS */ -#endif /* JS_HAS_OBJECT_LITERAL */ +#endif /* JS_HAS_INITIALIZERS */ + +#if JS_HAS_EXCEPTIONS + 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: + tn = script->trynotes; + offset = pc - script->code; + if (tn) { + for (; + tn->start && tn->end && (tn->start > offset || tn->end < offset); + tn++) + /* nop */ + ; + if (tn->catch) { + pc = script->code + tn->catch; + len = 0; + goto advance_pc; + } + } + + /* Not in a catch block, so propagate the exception. */ + fp->exceptPending = JS_TRUE; + fp->exception = POP(); + ok = JS_FALSE; + goto out; +#endif /* JS_HAS_EXCEPTIONS */ + +#if JS_HAS_INSTANCEOF + case JSOP_INSTANCEOF: + rval = POP(); + if (JSVAL_IS_PRIMITIVE(rval)) { + str = js_DecompileValueGenerator(cx, rval, NULL); + if (str) { + JS_ReportError(cx, "invalid %s operand %s", + js_instanceof_str, JS_GetStringBytes(str)); + } + ok = JS_FALSE; + goto out; + } + obj = JSVAL_TO_OBJECT(rval); + lval = POP(); + cond = JS_FALSE; + if (obj->map->ops->hasInstance) { + SAVE_SP(fp); + ok = obj->map->ops->hasInstance(cx, obj, lval, &cond); + if (!ok) + goto out; + } + PUSH_OPND(BOOLEAN_TO_JSVAL(cond)); + break; +#endif /* JS_HAS_INSTANCEOF */ default: JS_ReportError(cx, "unimplemented JavaScript bytecode %d", op); @@ -2381,48 +2565,30 @@ Interpret(JSContext *cx, jsval *result) #ifdef DEBUG if (tracefp) { intN ndefs, n; - jsval v; ndefs = cs->ndefs; if (ndefs) { fp->sp = sp - ndefs; - for (n = ndefs; n > 0; n--) { - v = sp[-n]; - str = js_ValueToSource(cx, v); + for (n = 0; n < ndefs; n++) { + str = js_DecompileValueGenerator(cx, *fp->sp, NULL); if (str) { fprintf(tracefp, "%s %s", - (n == ndefs) ? " output:" : ",", + (n == 0) ? " output:" : ",", JS_GetStringBytes(str)); } + fp->sp++; } putc('\n', tracefp); - SAVE_SP(fp); } } #endif } - /* - * Treat end of script as a backward branch to handle all kinds of return - * from function. - */ - CHECK_BRANCH(-1); - out: /* * Restore the previous frame's execution state. */ - PR_ARENA_RELEASE(&cx->stackPool, mark); - - if (dropAtom) { - JS_LOCK_RUNTIME_VOID(rt, js_DropAtom(cx, atom)); - dropAtom = JS_FALSE; - } - -#ifdef JS_THREADSAFE - ok &= JS_RemoveRoot(cx, &obj); -#endif - + js_FreeStack(cx, mark); cx->interpLevel--; return ok; } diff --git a/mozilla/js/src/jsinterp.h b/mozilla/js/src/jsinterp.h index 8d50077f14d..80d6cdfb328 100644 --- a/mozilla/js/src/jsinterp.h +++ b/mozilla/js/src/jsinterp.h @@ -28,7 +28,8 @@ * JS stack frame, allocated on the C stack. */ struct JSStackFrame { - JSObject *object; /* object for fun.arguments/fun.caller */ + JSObject *callobj; /* lazily created Call object */ + JSObject *argsobj; /* lazily created arguments object */ JSScript *script; /* script being interpreted */ JSFunction *fun; /* function being called or null */ JSObject *thisp; /* "this" pointer if in method */ @@ -44,6 +45,11 @@ struct JSStackFrame { jsval *sp; /* stack pointer */ uintN sharpDepth; /* array/object initializer depth */ JSObject *sharpArray; /* scope for #n= initializer vars */ + 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 */ }; /* @@ -56,11 +62,58 @@ struct JSStackFrame { #define PROPERTY_CACHE_HASH(obj, id) \ ((((pruword)(obj) >> JSVAL_TAGBITS) ^ (pruword)(id)) & PROPERTY_CACHE_MASK) -typedef struct JSPropertyCacheEntry { - JSProperty *property; /* weak link to property */ - jsval symbolid; /* strong link to name atom, or index jsval */ +#ifdef JS_THREADSAFE + +#if HAVE_ATOMIC_DWORD_ACCESS + +#define PCE_LOAD(cache, pce, entry) PR_ATOMIC_DWORD_LOAD(pce, entry) +#define PCE_STORE(cache, pce, entry) PR_ATOMIC_DWORD_STORE(pce, entry) + +#else /* !HAVE_ATOMIC_DWORD_ACCESS */ + +#define PCE_LOAD(cache, pce, entry) \ + PR_BEGIN_MACRO \ + uint32 _prefills; \ + uint32 _fills = (cache)->fills; \ + do { \ + /* Load until cache->fills is stable (see FILL macro below). */ \ + _prefills = _fills; \ + (entry) = *(pce); \ + } while ((_fills = (cache)->fills) != _prefills); \ + PR_END_MACRO + +#define PCE_STORE(cache, pce, entry) \ + PR_BEGIN_MACRO \ + do { \ + /* Store until no racing collider stores half or all of pce. */ \ + *(pce) = (entry); \ + } while (PCE_OBJECT(*pce) != PCE_OBJECT(entry) || \ + PCE_PROPERTY(*pce) != PCE_PROPERTY(entry)); \ + PR_END_MACRO + +#endif /* !HAVE_ATOMIC_DWORD_ACCESS */ + +#else /* !JS_THREADSAFE */ + +#define PCE_LOAD(cache, pce, entry) ((entry) = *(pce)) +#define PCE_STORE(cache, pce, entry) (*(pce) = (entry)) + +#endif /* !JS_THREADSAFE */ + +typedef union JSPropertyCacheEntry { + struct { + JSObject *object; /* weak link to object */ + JSProperty *property; /* weak link to property, or not-found id */ + } s; +#ifdef HAVE_ATOMIC_DWORD_ACCESS + prdword align; +#endif } JSPropertyCacheEntry; +/* These may be called in lvalue or rvalue position. */ +#define PCE_OBJECT(entry) ((entry).s.object) +#define PCE_PROPERTY(entry) ((entry).s.property) + typedef struct JSPropertyCache { JSPropertyCacheEntry table[PROPERTY_CACHE_SIZE]; JSBool empty; @@ -69,41 +122,49 @@ typedef struct JSPropertyCache { uint32 tests; uint32 misses; uint32 flushes; - uint32 pflushes; } JSPropertyCache; -#define PROP_NOT_FOUND ((JSProperty *)1) -#define PROP_FOUND(prop) ((prword)(prop) & (prword)-2) +/* Property-not-found lookup results are cached using this invalid pointer. */ +#define PROP_NOT_FOUND(obj,id) ((JSProperty *) ((prword)(id) | 1)) +#define PROP_NOT_FOUND_ID(prop) ((jsid) ((prword)(prop) & ~1)) +#define PROP_FOUND(prop) ((prop) && ((prword)(prop) & 1) == 0) #define PROPERTY_CACHE_FILL(cx, cache, obj, id, prop) \ PR_BEGIN_MACRO \ uintN _hashIndex = (uintN)PROPERTY_CACHE_HASH(obj, id); \ - JSPropertyCacheEntry *_pce = &(cache)->table[_hashIndex]; \ - if (_pce->property && _pce->property != prop) { \ - (cache)->recycles++; \ - if (!JSVAL_IS_INT(_pce->symbolid)) \ - js_DropAtom(cx, (JSAtom *)_pce->symbolid); \ - } \ - _pce->property = prop; \ - _pce->symbolid = id; \ - if (!JSVAL_IS_INT(id)) \ - js_HoldAtom(cx, (JSAtom *)id); \ - (cache)->empty = JS_FALSE; \ - (cache)->fills++; \ + JSPropertyCache *_cache = (cache); \ + JSPropertyCacheEntry *_pce = &_cache->table[_hashIndex]; \ + JSPropertyCacheEntry _entry; \ + JSProperty *_pce_prop; \ + PCE_LOAD(_cache, _pce, _entry); \ + _pce_prop = PCE_PROPERTY(_entry); \ + if (_pce_prop && _pce_prop != prop) \ + _cache->recycles++; \ + PCE_OBJECT(_entry) = obj; \ + PCE_PROPERTY(_entry) = prop; \ + _cache->empty = JS_FALSE; \ + _cache->fills++; \ + PCE_STORE(_cache, _pce, _entry); \ PR_END_MACRO #define PROPERTY_CACHE_TEST(cache, obj, id, prop) \ PR_BEGIN_MACRO \ uintN _hashIndex = (uintN)PROPERTY_CACHE_HASH(obj, id); \ - JSPropertyCacheEntry *_pce = &(cache)->table[_hashIndex]; \ - (cache)->tests++; \ - if (_pce->property && \ - (_pce->property == PROP_NOT_FOUND || \ - _pce->property->object == obj) && \ - _pce->symbolid == id) { \ - prop = _pce->property; \ + JSPropertyCache *_cache = (cache); \ + JSPropertyCacheEntry *_pce = &_cache->table[_hashIndex]; \ + JSPropertyCacheEntry _entry; \ + JSProperty *_pce_prop; \ + PCE_LOAD(_cache, _pce, _entry); \ + _pce_prop = PCE_PROPERTY(_entry); \ + _cache->tests++; \ + if (_pce_prop && \ + (((prword)_pce_prop & 1) \ + ? PROP_NOT_FOUND_ID(_pce_prop) \ + : sym_id(((JSScopeProperty *)_pce_prop)->symbols)) == id && \ + PCE_OBJECT(_entry) == obj) { \ + prop = _pce_prop; \ } else { \ - (cache)->misses++; \ + _cache->misses++; \ prop = NULL; \ } \ PR_END_MACRO @@ -114,6 +175,12 @@ js_FlushPropertyCache(JSContext *cx); extern void js_FlushPropertyCacheByProp(JSContext *cx, JSProperty *prop); +extern jsval * +js_AllocStack(JSContext *cx, uintN nslots, void **markp); + +extern void +js_FreeStack(JSContext *cx, void *mark); + extern JSBool js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); @@ -127,14 +194,17 @@ extern JSBool js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JSBool -js_DoCall(JSContext *cx, uintN argc); +js_Invoke(JSContext *cx, uintN argc, JSBool constructing); extern JSBool -js_Call(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, - jsval *rval); +js_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, + uintN argc, jsval *argv, jsval *rval); extern JSBool -js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSStackFrame *down, - jsval *result); +js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSFunction *fun, + JSStackFrame *down, JSBool debugging, jsval *result); + +extern JSBool +js_Interpret(JSContext *cx, jsval *result); #endif /* jsinterp_h___ */ diff --git a/mozilla/js/src/jslock.c b/mozilla/js/src/jslock.c index e54c2868c70..5092a8b3ce2 100644 --- a/mozilla/js/src/jslock.c +++ b/mozilla/js/src/jslock.c @@ -16,104 +16,749 @@ * Reserved. */ -#include -#include -#include "jslock.h" - #ifdef JS_THREADSAFE -#include "prlog.h" -#include "prlock.h" -#include "prthread.h" -#include "prlong.h" - -PRLock* js_owner_lock = 0; -PRThread *js_owner_thread = 0; -unsigned js_owner_count = 0; - -void -js_lock_task(JSTaskState *task) -{ - PRThread *me = PR_GetCurrentThread(); - - if (! js_owner_lock) { - js_owner_lock = PR_NewLock(); - } - - if ( js_owner_thread == me) { - PR_ASSERT(js_owner_count > 0); - js_owner_count++; - } - else { - PR_Lock(js_owner_lock); - PR_ASSERT(js_owner_count == 0); - js_owner_count = 1; - js_owner_thread = me; - } - -} - -void -js_unlock_task(JSTaskState *task) -{ - if (js_owner_thread != PR_GetCurrentThread()) - return; - - if ( --js_owner_count == 0) { - js_owner_thread = 0; - PR_Unlock(js_owner_lock); - } - PR_ASSERT(js_owner_count >= 0); -} - /* -PRMonitor *js_owner_monitor; -PRThread *js_owner_thread; -unsigned js_owner_count; + * JS locking stubs. + */ +#include +#include "jspubtd.h" +#include "prthread.h" +#include "prlog.h" +#include "jscntxt.h" +#include "jsscope.h" +#include "jspubtd.h" +#include "jslock.h" -void -js_lock_task(JSTaskState *task) +static PRLock *_global_lock; + +static void +js_LockGlobal() { - PRThread *t = PR_CurrentThread(); + PR_Lock(_global_lock); +} - if (!js_owner_monitor) - js_owner_monitor = PR_NewNamedMonitor("js-owner-monitor"); - PR_EnterMonitor(js_owner_monitor); +static void +js_UnlockGlobal() +{ + PR_Unlock(_global_lock); +} - if (js_owner_thread == t) { - js_owner_count++; - } else { - do { - if (!js_owner_thread) { - js_owner_thread = t; - break; - } - PR_Wait(js_owner_monitor, PR_INTERVAL_MAX); - } while (js_owner_thread != t); +#define AtomicRead(W) (W) +#define AtomicAddBody(P,I)\ + prword n;\ + do {\ + n = AtomicRead(*(P));\ + } while (!js_CompareAndSwap(P, n, n + I)); + +#if defined(_WIN32) && !defined(NSPR_LOCK) +#pragma warning( disable : 4035 ) + +PR_INLINE int +js_CompareAndSwap(prword *w, prword ov, prword nv) +{ + __asm { + mov eax,ov + mov ecx, nv + mov ebx, w + lock cmpxchg [ebx], ecx + sete al + and eax,1h + } +} + +#elif defined(SOLARIS) && !defined(NSPR_LOCK) + +#ifndef ULTRA_SPARC +PR_INLINE prword +js_AtomicRead(prword *p) +{ + prword n; + while ((n = *p) == -1) + ; + return n; +} + +#undef AtomicRead +#define AtomicRead(W) js_AtomicRead(&(W)) + +static PRLock *_counter_lock; +#define UsingCounterLock 1 + +#undef AtomicAddBody +#define AtomicAddBody(P,I) \ + PR_Lock(_counter_lock);\ + *(P) += I;\ + PR_Unlock(_counter_lock); + +#endif /* !ULTRA_SPARC */ + +PR_INLINE int +js_CompareAndSwap(prword *w, prword ov, prword nv) +{ +#if defined(__GNUC__) + unsigned int res; +#ifndef ULTRA_SPARC + PR_ASSERT(nv >= 0); + asm volatile (" +swap [%1],%4 +1: tst %4 +bneg,a 1b +swap [%1],%4 +cmp %2,%4 +be,a 2f +swap [%1],%3 +swap [%1],%4 +ba 3f +mov 0,%0 +2: mov 1,%0 +3:" + : "=r" (res) + : "r" (w), "r" (ov), "r" (nv), "r" (-1)); +#else /* ULTRA_SPARC */ + PR_ASSERT(ov != nv); + asm volatile (" +cas [%1],%2,%3 +cmp %2,%3 +be,a 1f +mov 1,%0 +mov 0,%0 +1:" + : "=r" (res) + : "r" (w), "r" (ov), "r" (nv)); +#endif /* ULTRA_SPARC */ + return (int)res; +#else /* !__GNUC__ */ + extern int compare_and_swap(prword*,prword,prword); +#ifndef ULTRA_SPARC + PR_ASSERT(nv >= 0); +#else + PR_ASSERT(ov != nv); +#endif + return compare_and_swap(w,ov,nv); +#endif +} + +#elif defined(AIX) && !defined(NSPR_LOCK) + +PR_INLINE int +js_CompareAndSwap(prword *w, prword ov, prword nv) +{ +/* + int i = -1; + + lwarx reg,0,w + cmpw reg,ov + bne no + stwcx. nv,0,w + beq yes + no: + li res,0 + yes: + */ + extern int compare_and_swap(int*,int*,int); + + return compare_and_swap(w,&ov,nv); +} + +#else + +static PRLock *_counter_lock; +#define UsingCounterLock 1 + +#undef AtomicAddBody +#define AtomicAddBody(P,I) \ + PR_Lock(_counter_lock);\ + *(P) += I;\ + PR_Unlock(_counter_lock); + +static PRLock *_compare_and_swap_lock; +#define UsingCompareAndSwapLock 1 + +PR_INLINE int +js_CompareAndSwap(prword *w, prword ov, prword nv) +{ + int res = 0; + + PR_Lock(_compare_and_swap_lock); + if (*w == ov) { + *w = nv; + res = 1; } - PR_ExitMonitor(js_owner_monitor); + PR_Unlock(_compare_and_swap_lock); + return res; +} + +#endif /* arch-tests */ + +PR_INLINE void +js_AtomicAdd(prword *p, prword i) +{ + AtomicAddBody(p,i); +} + +prword +js_CurrentThreadId() +{ + return CurrentThreadId(); } void -js_unlock_task(JSTaskState *task) +js_NewLock(JSThinLock *p) { - PR_ASSERT(PR_CurrentThread() == js_owner_thread); - PR_EnterMonitor(js_owner_monitor); - if (js_owner_count) { - js_owner_count--; - } else { - js_owner_thread = NULL; - PR_Notify(js_owner_monitor); - } - PR_ExitMonitor(js_owner_monitor); +#ifdef NSPR_LOCK + p->owner = 0; + p->fat = (JSFatLock*)JS_NEW_LOCK(); +#else + memset(p, 0, sizeof(JSThinLock)); +#endif } + +void +js_DestroyLock(JSThinLock *p) +{ +#ifdef NSPR_LOCK + p->owner = 0xdeadbeef; + JS_DESTROY_LOCK(((JSLock*)p->fat)); +#else + PR_ASSERT(p->owner == 0); + PR_ASSERT(p->fat == NULL); +#endif +} + +static void js_Dequeue(JSThinLock *); + +PR_INLINE jsval +js_GetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot) +{ + jsval v; +#ifndef NSPR_LOCK + JSScope *scp = (JSScope *)obj->map; + JSThinLock *p = &scp->lock; + prword me = cx->thread; +#endif + + PR_ASSERT(obj->slots && slot < obj->map->freeslot); +#ifndef NSPR_LOCK + PR_ASSERT(me == CurrentThreadId()); + if (js_CompareAndSwap(&p->owner, 0, me)) { + if (scp == (JSScope *)obj->map) { + v = obj->slots[slot]; + if (!js_CompareAndSwap(&p->owner, me, 0)) { + scp->count = 1; + js_UnlockObj(cx,obj); + } + return v; + } + if (!js_CompareAndSwap(&p->owner, me, 0)) + js_Dequeue(p); + } + else if (Thin_RemoveWait(AtomicRead(p->owner)) == me) { + return obj->slots[slot]; + } +#endif + js_LockObj(cx,obj); + v = obj->slots[slot]; + js_UnlockObj(cx,obj); + return v; +} + +PR_INLINE void +js_SetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot, jsval v) +{ +#ifndef NSPR_LOCK + JSScope *scp = (JSScope *)obj->map; + JSThinLock *p = &scp->lock; + prword me = cx->thread; +#endif + + PR_ASSERT(obj->slots && slot < obj->map->freeslot); +#ifndef NSPR_LOCK + PR_ASSERT(me == CurrentThreadId()); + if (js_CompareAndSwap(&p->owner, 0, me)) { + if (scp == (JSScope *)obj->map) { + obj->slots[slot] = v; + if (!js_CompareAndSwap(&p->owner, me, 0)) { + scp->count = 1; + js_UnlockObj(cx,obj); + } + return; + } + if (!js_CompareAndSwap(&p->owner, me, 0)) + js_Dequeue(p); + } + else if (Thin_RemoveWait(AtomicRead(p->owner)) == me) { + obj->slots[slot] = v; + return; + } +#endif + js_LockObj(cx,obj); + obj->slots[slot] = v; + js_UnlockObj(cx,obj); +} + +static JSFatLock * +mallocFatlock() +{ + JSFatLock *fl = (JSFatLock *)malloc(sizeof(JSFatLock)); /* for now */ + PR_ASSERT(fl); + fl->susp = 0; + fl->next = NULL; + fl->prev = NULL; + fl->slock = PR_NewLock(); + fl->svar = PR_NewCondVar(fl->slock); + return fl; +} + +static void +freeFatlock(JSFatLock *fl) +{ + PR_DestroyLock(fl->slock); + PR_DestroyCondVar(fl->svar); + free(fl); +} + +static int +js_SuspendThread(JSThinLock *p) +{ + JSFatLock *fl = p->fat; + PRStatus stat; + if (fl == NULL) + return 1; + PR_Lock(fl->slock); + fl->susp++; + if (p->fat == NULL || fl->susp<1) { + PR_Unlock(fl->slock); + return 1; + } + stat = PR_WaitCondVar(fl->svar,PR_INTERVAL_NO_TIMEOUT); + if (stat == PR_FAILURE) { + fl->susp--; + return 0; + } + PR_Unlock(fl->slock); + return 1; +} + +static void +js_ResumeThread(JSThinLock *p) +{ + JSFatLock *fl = p->fat; + PRStatus stat; + PR_Lock(fl->slock); + fl->susp--; + stat = PR_NotifyCondVar(fl->svar); + PR_ASSERT(stat != PR_FAILURE); + PR_Unlock(fl->slock); +} + +static JSFatLock * +listOfFatlocks(int l) +{ + JSFatLock *m; + JSFatLock *m0; + int i; + + PR_ASSERT(l>0); + m0 = m = mallocFatlock(); + for (i=1; inext = mallocFatlock(); + m = m->next; + } + return m0; +} + +static void +deleteListOfFatlocks(JSFatLock *m) +{ + JSFatLock *m0; + for (; m; m=m0) { + m0 = m->next; + freeFatlock(m); + } +} + +static JSFatLockTable _fl_table; + +JSFatLock * +allocateFatlock() +{ + JSFatLock *m; + + if (_fl_table.free == NULL) { +#ifdef DEBUG + printf("Ran out of fat locks!\n"); +#endif + _fl_table.free = listOfFatlocks(10); + } + m = _fl_table.free; + _fl_table.free = m->next; + _fl_table.free->prev = NULL; + m->susp = 0; + m->next = _fl_table.taken; + m->prev = NULL; + if (_fl_table.taken != NULL) + _fl_table.taken->prev = m; + _fl_table.taken = m; + return m; +} + +void +deallocateFatlock(JSFatLock *m) +{ + if (m == NULL) + return; + if (m->prev != NULL) + m->prev->next = m->next; + if (m->next != NULL) + m->next->prev = m->prev; + if (m == _fl_table.taken) + _fl_table.taken = m->next; + m->next = _fl_table.free; + _fl_table.free = m; +} + +JS_PUBLIC_API(int) +js_SetupLocks(int l) +{ + if (l > 10000) /* l equals number of initially allocated fat locks */ +#ifdef DEBUG + printf("Number %d very large in js_SetupLocks()!\n",l); +#endif + if (_global_lock) + return 1; + _global_lock = PR_NewLock(); + PR_ASSERT(_global_lock); +#ifdef UsingCounterLock + _counter_lock = PR_NewLock(); + PR_ASSERT(_counter_lock); +#endif +#ifdef UsingCompareAndSwapLock + _compare_and_swap_lock = PR_NewLock(); + PR_ASSERT(_compare_and_swap_lock); +#endif + _fl_table.free = listOfFatlocks(l); + _fl_table.taken = NULL; + return 1; +} + +JS_PUBLIC_API(void) +js_CleanupLocks() +{ + if (_global_lock != NULL) { + deleteListOfFatlocks(_fl_table.free); + _fl_table.free = NULL; + deleteListOfFatlocks(_fl_table.taken); + _fl_table.taken = NULL; + PR_DestroyLock(_global_lock); + _global_lock = NULL; +#ifdef UsingCounterLock + PR_DestroyLock(_counter_lock); + _counter_lock = NULL; +#endif +#ifdef UsingCompareAndSwapLock + PR_DestroyLock(_compare_and_swap_lock); + _compare_and_swap_lock = NULL; +#endif + } +} + +JS_PUBLIC_API(void) +js_InitContextForLocking(JSContext *cx) +{ + cx->thread = CurrentThreadId(); + PR_ASSERT(Thin_GetWait(cx->thread) == 0); +} + +/* + + It is important that emptyFatlock() clears p->fat in the empty case + while holding p->slock. This serializes the access of p->fat wrt + js_SuspendThread(), which also requires p->slock. There would + otherwise be a race condition as follows. Thread A is about to + clear p->fat, having woken up after being suspended in a situation + where no other thread has yet suspended on p. However, suppose + thread B is about to suspend on p, having just released the global + lock, and is currently calling js_SuspendThread(). In the unfortunate + case where A precedes B ever so slightly, A will think that it + should clear p->fat (being currently empty but not for long), at the + same time as B is about to suspend on p->fat. Now, A deallocates + p->fat while B is about to suspend on it. Thus, the suspension of B + is lost and B will not be properly activated. + + Using p->slock as below (and correspondingly in js_SuspendThread()), + js_SuspendThread() will notice that p->fat is empty, and hence return + immediately. + + */ + +int +emptyFatlock(JSThinLock *p) +{ + JSFatLock *fl = p->fat; + int i; + + if (fl == NULL) + return 1; + PR_Lock(fl->slock); + i = fl->susp; + if (i < 1) { + fl->susp = -1; + deallocateFatlock(fl); + p->fat = NULL; + } + PR_Unlock(fl->slock); + 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 + by storing A's identity in the object being locked. + + If another thread B tries to lock the object currently locked by A, + B is enqueued into a fat lock structure (which might have to be + allocated and pointed to by the object), and suspended using NSPR + conditional variables (wait). A wait bit (Bacon bit) is set in the + lock word of the object, signalling to A that when releasing the + lock, B must be dequeued and notified. + + The basic operation of the locking primitives (js_Lock(), + js_Unlock(), js_Enqueue(), and js_Dequeue()) is + compare-and-swap. Hence, when locking into p, if + compare-and-swap(p,NULL,me) succeeds this implies that p is + unlocked. Similarly, when unlocking p, if + compare-and-swap(p,me,NULL) succeeds this implies that p is + uncontended (no one is waiting because the wait bit is not set). + + Furthermore, when enqueueing (after the compare-and-swap has failed + to lock the object), a global spin lock is grabbed which serializes + changes to an object, and prevents the race condition when a lock is + released simultaneously with a thread suspending on it (these two + operations are serialized using the spin lock). Thus, a thread is + enqueued if the lock is still held by another thread, and otherwise + the lock is grabbed using compare-and-swap. When dequeueing, the + lock is released, and one of the threads suspended on the lock is + notified. If other threads still are waiting, the wait bit is kept, and if + not, the fat lock is deallocated (in emptyFatlock()). + */ -#ifdef DEBUG -int -js_is_task_locked(JSTaskState *task) +static void +js_Enqueue(JSThinLock *p, prword me) { - return (PR_CurrentThread() == js_owner_thread); + prword o, n; + + js_LockGlobal(); + while (1) { + o = AtomicRead(p->owner); + n = Thin_SetWait(o); + if (o != 0 && js_CompareAndSwap(&p->owner,o,n)) { + if (p->fat == NULL) + p->fat = allocateFatlock(); +#ifdef DEBUG_ + printf("Going to suspend (%x) on {%x,%x}\n",me,p,p->fat); +#endif + js_UnlockGlobal(); + js_SuspendThread(p); + js_LockGlobal(); +#ifdef DEBUG_ + printf("Waking up (%x) on {%x,%x,%d}\n",me,p,p->fat,(p->fat ? p->fat->susp : 42)); +#endif + if (emptyFatlock(p)) + me = Thin_RemoveWait(me); + else + me = Thin_SetWait(me); + } + else if (js_CompareAndSwap(&p->owner,0,me)) { + js_UnlockGlobal(); + return; + } + } +} + +static void +js_Dequeue(JSThinLock *p) +{ + js_LockGlobal(); + PR_ASSERT(Thin_GetWait(AtomicRead(p->owner))); + p->owner = 0; /* release it */ + PR_ASSERT(p->fat); + js_ResumeThread(p); + js_UnlockGlobal(); +} + +PR_INLINE void +js_Lock(JSThinLock *p, prword me) +{ +#ifdef DEBUG_ + printf("\nT%x about to lock %x\n",me,p); +#endif + PR_ASSERT(me == CurrentThreadId()); + if (js_CompareAndSwap(&p->owner, 0, me)) + return; + if (Thin_RemoveWait(AtomicRead(p->owner)) != me) + js_Enqueue(p, me); +#ifdef DEBUG + else + PR_ASSERT(0); +#endif +} + +PR_INLINE void +js_Unlock(JSThinLock *p, prword me) +{ +#ifdef DEBUG_ + printf("\nT%x about to unlock %p\n",me,p); +#endif + PR_ASSERT(me == CurrentThreadId()); + if (js_CompareAndSwap(&p->owner, me, 0)) + return; + if (Thin_RemoveWait(AtomicRead(p->owner)) == me) + js_Dequeue(p); +#ifdef DEBUG + else + PR_ASSERT(0); +#endif +} + +void +js_LockRuntime(JSRuntime *rt) +{ + prword me = CurrentThreadId(); + JSThinLock *p; + + PR_ASSERT(Thin_RemoveWait(AtomicRead(rt->rtLock.owner)) != me); + p = &rt->rtLock; + JS_LOCK0(p,me); +} + +void +js_UnlockRuntime(JSRuntime *rt) +{ + prword me = CurrentThreadId(); + JSThinLock *p; + + PR_ASSERT(Thin_RemoveWait(AtomicRead(rt->rtLock.owner)) == me); + p = &rt->rtLock; + JS_UNLOCK0(p,me); +} + +PR_INLINE void +js_LockScope1(JSContext *cx, JSScope *scope, prword me) +{ + JSThinLock *p; + + if (Thin_RemoveWait(AtomicRead(scope->lock.owner)) == me) { + PR_ASSERT(scope->count > 0); + scope->count++; + } else { + p = &scope->lock; + JS_LOCK0(p,me); + PR_ASSERT(scope->count == 0); + scope->count = 1; + } +} + +void +js_LockScope(JSContext *cx, JSScope *scope) +{ + PR_ASSERT(cx->thread == CurrentThreadId()); + js_LockScope1(cx,scope,cx->thread); +} + +void +js_UnlockScope(JSContext *cx, JSScope *scope) +{ + prword me = cx->thread; + JSThinLock *p; + + PR_ASSERT(scope->count > 0); + if (Thin_RemoveWait(AtomicRead(scope->lock.owner)) != me) { + PR_ASSERT(0); + return; + } + if (--scope->count == 0) { + p = &scope->lock; + JS_UNLOCK0(p,me); + } +} + +void +js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope) +{ + prword me; + JSThinLock *p; + + PR_ASSERT(JS_IS_SCOPE_LOCKED(newscope)); + /* + * If the last reference to oldscope went away, newscope needs no lock + * state update. + */ + if (!oldscope) { + return; + } + PR_ASSERT(JS_IS_SCOPE_LOCKED(oldscope)); + /* + * Transfer oldscope's entry count to newscope, as it will be unlocked + * now via JS_UNLOCK_OBJ(cx,obj) calls made while we unwind the C stack + * from the current point (under js_GetMutableScope). + */ + newscope->count = oldscope->count; + /* + * Reset oldscope's lock state so that it is completely unlocked. + */ + oldscope->count = 0; + p = &oldscope->lock; + me = cx->thread; + JS_UNLOCK0(p,me); +} + +void +js_LockObj(JSContext *cx, JSObject *obj) +{ + JSScope *scope; + prword me = cx->thread; + PR_ASSERT(me == CurrentThreadId()); + for (;;) { + scope = (JSScope *) obj->map; + js_LockScope1(cx, scope, me); + + /* If obj still has this scope, we're done. */ + if (scope == (JSScope *) obj->map) + return; + + /* Lost a race with a mutator; retry with obj's new scope. */ + js_UnlockScope(cx, scope); + } +} + +void +js_UnlockObj(JSContext *cx, JSObject *obj) +{ + js_UnlockScope(cx, (JSScope *) obj->map); +} + +#ifdef DEBUG +JSBool +js_IsRuntimeLocked(JSRuntime *rt) +{ + return CurrentThreadId() == Thin_RemoveWait(AtomicRead(rt->rtLock.owner)); +} + +JSBool +js_IsObjLocked(JSObject *obj) +{ + JSObjectMap *map = obj->map; + + return MAP_IS_NATIVE(map) && CurrentThreadId() == Thin_RemoveWait(AtomicRead(((JSScope *)map)->lock.owner)); +} + +JSBool +js_IsScopeLocked(JSScope *scope) +{ + return CurrentThreadId() == Thin_RemoveWait(AtomicRead(scope->lock.owner)); } #endif diff --git a/mozilla/js/src/jslock.h b/mozilla/js/src/jslock.h index 4ace67e04c2..671f441a49e 100644 --- a/mozilla/js/src/jslock.h +++ b/mozilla/js/src/jslock.h @@ -15,70 +15,203 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ - -#ifndef jslock_h___ -#define jslock_h___ -/* - * JS locking macros, used to protect globals only if JS_THREADSAFE is defined. - */ +#ifndef jslock_h__ +#define jslock_h__ #ifdef JS_THREADSAFE +#include "prtypes.h" #include "prlock.h" -#include "jscntxt.h" - -#define JS_ACQUIRE_LOCK(l) PR_Lock(l) -#define JS_RELEASE_LOCK(l) PR_Unlock(l) - -#define JS_LOCK_RUNTIME(rt) js_lock_runtime(rt) -#define JS_UNLOCK_RUNTIME(rt) js_unlock_runtime(rt) - -extern void js_lock_runtime(JSRuntime *rt); -extern void js_unlock_runtime(JSRuntime *rt); -#ifdef DEBUG -#define JS_IS_RUNTIME_LOCKED(rt) js_is_runtime_locked(rt) - -extern int js_is_runtime_locked(JSRuntime *rt); +#include "prcvar.h" +#ifndef NSPR20 +#include "prhash.h" #else -#define JS_IS_RUNTIME_LOCKED(rt) 1 +#include "plhash.h" #endif +#define Thin_GetWait(W) ((prword)(W) & 0x1) +#define Thin_SetWait(W) ((prword)(W) | 0x1) +#define Thin_RemoveWait(W) ((prword)(W) & ~0x1) + +typedef struct JSFatLock { + int susp; + PRLock* slock; + PRCondVar* svar; + struct JSFatLock *next; + struct JSFatLock *prev; +} JSFatLock; + +typedef struct JSThinLock { + prword owner; + JSFatLock *fat; +} JSThinLock; + +typedef PRLock JSLock; + +typedef struct JSFatLockTable { + JSFatLock *free; + JSFatLock *taken; +} JSFatLockTable; + +#define JS_ATOMIC_ADDREF(p, i) js_AtomicAdd(p,i) + +#define CurrentThreadId() (prword)PR_GetCurrentThread() +#define JS_CurrentThreadId() js_CurrentThreadId() +#define JS_NEW_LOCK() PR_NewLock() +#define JS_DESTROY_LOCK(l) PR_DestroyLock(l) +#define JS_ACQUIRE_LOCK(l) PR_Lock(l) +#define JS_RELEASE_LOCK(l) PR_Unlock(l) +#define JS_LOCK0(P,M) js_Lock(P,M) +#define JS_UNLOCK0(P,M) js_Unlock(P,M) + +#define JS_NEW_CONDVAR(l) PR_NewCondVar(l) +#define JS_DESTROY_CONDVAR(cv) PR_DestroyCondVar(cv) +#define JS_WAIT_CONDVAR(cv,to) PR_WaitCondVar(cv,to) +#define JS_NO_TIMEOUT PR_INTERVAL_NO_TIMEOUT +#define JS_NOTIFY_CONDVAR(cv) PR_NotifyCondVar(cv) +#define JS_NOTIFY_ALL_CONDVAR(cv) PR_NotifyAllCondVar(cv) + +#ifdef DEBUG +#include "jsscope.h" + +#define _SET_OBJ_INFO(obj,f,l) \ + _SET_SCOPE_INFO(((JSScope*)obj->map),f,l) + +#define _SET_SCOPE_INFO(scope,f,l) \ + (PR_ASSERT(scope->count > 0 && scope->count <= 4), \ + scope->file[scope->count-1] = f, \ + scope->line[scope->count-1] = l) +#endif /* DEBUG */ + +#define JS_LOCK_RUNTIME(rt) js_LockRuntime(rt) +#define JS_UNLOCK_RUNTIME(rt) js_UnlockRuntime(rt) +#define JS_LOCK_OBJ(cx,obj) (js_LockObj(cx, obj), \ + _SET_OBJ_INFO(obj,__FILE__,__LINE__)) +#define JS_UNLOCK_OBJ(cx,obj) js_UnlockObj(cx, obj) +#define JS_LOCK_SCOPE(cx,scope) (js_LockScope(cx, scope), \ + _SET_SCOPE_INFO(scope,__FILE__,__LINE__)) +#define JS_UNLOCK_SCOPE(cx,scope) js_UnlockScope(cx, scope) +#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) js_TransferScopeLock(cx, scope, newscope) + +extern prword js_CurrentThreadId(); +extern PR_INLINE void js_Lock(JSThinLock *, prword); +extern PR_INLINE void js_Unlock(JSThinLock *, prword); +extern int js_CompareAndSwap(prword *, prword, prword); +extern void js_AtomicAdd(prword*, prword); +extern void js_LockRuntime(JSRuntime *rt); +extern void js_UnlockRuntime(JSRuntime *rt); +extern void js_LockObj(JSContext *cx, JSObject *obj); +extern void js_UnlockObj(JSContext *cx, JSObject *obj); +extern void js_LockScope(JSContext *cx, JSScope *scope); +extern void js_UnlockScope(JSContext *cx, JSScope *scope); +extern JS_PUBLIC_API(int) js_SetupLocks(int); +extern JS_PUBLIC_API(void) js_CleanupLocks(); +extern JS_PUBLIC_API(void) js_InitContextForLocking(JSContext *); +extern void js_TransferScopeLock(JSContext *, JSScope *, JSScope *); +extern JS_PUBLIC_API(jsval) js_GetSlotWhileLocked(JSContext *, JSObject *, uint32); +extern JS_PUBLIC_API(void) js_SetSlotWhileLocked(JSContext *, JSObject *, uint32, jsval); +extern void js_NewLock(JSThinLock *); +extern void js_DestroyLock(JSThinLock *); + +#ifdef DEBUG + +#define JS_IS_RUNTIME_LOCKED(rt) js_IsRuntimeLocked(rt) +#define JS_IS_OBJ_LOCKED(obj) js_IsObjLocked(obj) +#define JS_IS_SCOPE_LOCKED(scope) js_IsScopeLocked(scope) + +extern JSBool js_IsRuntimeLocked(JSRuntime *rt); +extern JSBool js_IsObjLocked(JSObject *obj); +extern JSBool js_IsScopeLocked(JSScope *scope); + +#else + +#define JS_IS_RUNTIME_LOCKED(rt) 0 +#define JS_IS_OBJ_LOCKED(obj) 1 +#define JS_IS_SCOPE_LOCKED(scope) 1 + +#endif /* DEBUG */ + +#define JS_LOCK_OBJ_VOID(cx, obj, e) \ + PR_BEGIN_MACRO \ + js_LockObj(cx, obj); \ + e; \ + js_UnlockObj(cx, obj); \ + PR_END_MACRO + #define JS_LOCK_VOID(cx, e) \ PR_BEGIN_MACRO \ JSRuntime *_rt = (cx)->runtime; \ JS_LOCK_RUNTIME_VOID(_rt, e); \ PR_END_MACRO +#if defined(JS_ONLY_NSPR_LOCKS) || !(defined(_WIN32) || defined(SOLARIS) || defined(AIX)) + +#undef JS_LOCK0 +#undef JS_UNLOCK0 +#define JS_LOCK0(P,M) JS_ACQUIRE_LOCK(((JSLock*)(P)->fat)); (P)->owner = (M) +#define JS_UNLOCK0(P,M) (P)->owner = 0; JS_RELEASE_LOCK(((JSLock*)(P)->fat)) +#define NSPR_LOCK 1 + +#endif /* arch-tests */ + #else /* !JS_THREADSAFE */ -#define JS_ACQUIRE_LOCK(l) ((void)0) -#define JS_RELEASE_LOCK(l) ((void)0) +#define JS_ATOMIC_ADDREF(p,i) (*(p) += i) -#define JS_LOCK_RUNTIME(rt) ((void)0) -#define JS_UNLOCK_RUNTIME(rt) ((void)0) -#define JS_IS_RUNTIME_LOCKED(rt) 1 -#define JS_LOCK_VOID(cx, e) JS_LOCK_RUNTIME_VOID((cx)->runtime, e) +#define JS_CurrentThreadId() 0 +#define JS_NEW_LOCK() NULL +#define JS_DESTROY_LOCK(l) ((void)0) +#define JS_ACQUIRE_LOCK(l) ((void)0) +#define JS_RELEASE_LOCK(l) ((void)0) +#define JS_LOCK0(P,M) ((void)0) +#define JS_UNLOCK0(P,M) ((void)0) + +#define JS_NEW_CONDVAR(l) NULL +#define JS_DESTROY_CONDVAR(cv) ((void)0) +#define JS_WAIT_CONDVAR(cv,to) ((void)0) +#define JS_NOTIFY_CONDVAR(cv) ((void)0) +#define JS_NOTIFY_ALL_CONDVAR(cv) ((void)0) + +#define JS_LOCK_RUNTIME(rt) ((void)0) +#define JS_UNLOCK_RUNTIME(rt) ((void)0) +#define JS_LOCK_OBJ(cx,obj) ((void)0) +#define JS_UNLOCK_OBJ(cx,obj) ((void)0) +#define JS_LOCK_OBJ_VOID(cx,obj,e) (e) +#define JS_LOCK_SCOPE(cx,scope) ((void)0) +#define JS_UNLOCK_SCOPE(cx,scope) ((void)0) +#define JS_TRANSFER_SCOPE_LOCK(c,o,n) ((void)0) + +#define JS_IS_RUNTIME_LOCKED(rt) 1 +#define JS_IS_OBJ_LOCKED(obj) 1 +#define JS_IS_SCOPE_LOCKED(scope) 1 +#define JS_LOCK_VOID(cx, e) JS_LOCK_RUNTIME_VOID((cx)->runtime, e) #endif /* !JS_THREADSAFE */ -#define JS_LOCK(cx) JS_LOCK_RUNTIME((cx)->runtime) -#define JS_UNLOCK(cx) JS_UNLOCK_RUNTIME((cx)->runtime) -#define JS_IS_LOCKED(cx) JS_IS_RUNTIME_LOCKED((cx)->runtime) - -#define JS_LOCK_RUNTIME_VOID(t,e) \ - (JS_LOCK_RUNTIME(t), (void)(e), JS_UNLOCK_RUNTIME(t)) - -#define JS_LOCK_AND_RETURN_TYPE(cx, type, call) \ +#define JS_LOCK_RUNTIME_VOID(rt,e) \ PR_BEGIN_MACRO \ - type _ret; \ - JS_LOCK_VOID(cx, _ret = call); \ - return _ret; \ + JS_LOCK_RUNTIME(rt); \ + e; \ + JS_UNLOCK_RUNTIME(rt); \ PR_END_MACRO -#define JS_LOCK_AND_RETURN_BOOL(cx, call) \ - JS_LOCK_AND_RETURN_TYPE(cx, JSBool, call) +#define JS_LOCK_GC(rt) JS_ACQUIRE_LOCK((rt)->gcLock) +#define JS_UNLOCK_GC(rt) JS_RELEASE_LOCK((rt)->gcLock) +#define JS_LOCK_GC_VOID(rt,e) (JS_LOCK_GC(rt), (e), JS_UNLOCK_GC(rt)) +#define JS_AWAIT_GC_DONE(rt) JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT) +#define JS_NOTIFY_GC_DONE(rt) JS_NOTIFY_ALL_CONDVAR((rt)->gcDone) +#define JS_AWAIT_REQUEST_DONE(rt) JS_WAIT_CONDVAR((rt)->requestDone, \ + JS_NO_TIMEOUT) +#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone) -#define JS_LOCK_AND_RETURN_STRING(cx, call) \ - JS_LOCK_AND_RETURN_TYPE(cx, JSString *, call) +#define JS_LOCK(P,CX) JS_LOCK0(P,(CX)->thread) +#define JS_UNLOCK(P,CX) JS_UNLOCK0(P,(CX)->thread) + +#ifndef _SET_OBJ_INFO +#define _SET_OBJ_INFO(obj,f,l) ((void)0) +#endif +#ifndef _SET_SCOPE_INFO +#define _SET_SCOPE_INFO(scope,f,l) ((void)0) +#endif #endif /* jslock_h___ */ diff --git a/mozilla/js/src/jsmath.c b/mozilla/js/src/jsmath.c index a96af8008d6..4234956466e 100644 --- a/mozilla/js/src/jsmath.c +++ b/mozilla/js/src/jsmath.c @@ -27,6 +27,8 @@ #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" +#include "jsconfig.h" +#include "jslock.h" #include "jsmath.h" #include "jsnum.h" #include "jsobj.h" @@ -80,7 +82,7 @@ math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = (x < 0) ? -x : x; return js_NewNumberValue(cx, z, rval); @@ -91,7 +93,7 @@ math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = acos(x); return js_NewNumberValue(cx, z, rval); @@ -102,7 +104,7 @@ math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = asin(x); return js_NewNumberValue(cx, z, rval); @@ -113,7 +115,7 @@ math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = atan(x); return js_NewNumberValue(cx, z, rval); @@ -124,9 +126,9 @@ math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, y, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; - if (!JS_ValueToNumber(cx, argv[1], &y)) + if (!js_ValueToNumber(cx, argv[1], &y)) return JS_FALSE; z = atan2(x, y); return js_NewNumberValue(cx, z, rval); @@ -137,7 +139,7 @@ math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = ceil(x); return js_NewNumberValue(cx, z, rval); @@ -148,7 +150,7 @@ math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = cos(x); return js_NewNumberValue(cx, z, rval); @@ -159,7 +161,7 @@ math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = exp(x); return js_NewNumberValue(cx, z, rval); @@ -170,7 +172,7 @@ math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = floor(x); return js_NewNumberValue(cx, z, rval); @@ -181,7 +183,7 @@ math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = log(x); return js_NewNumberValue(cx, z, rval); @@ -192,9 +194,9 @@ math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, y, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; - if (!JS_ValueToNumber(cx, argv[1], &y)) + if (!js_ValueToNumber(cx, argv[1], &y)) return JS_FALSE; z = (x > y) ? x : y; return js_NewNumberValue(cx, z, rval); @@ -205,9 +207,9 @@ math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, y, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; - if (!JS_ValueToNumber(cx, argv[1], &y)) + if (!js_ValueToNumber(cx, argv[1], &y)) return JS_FALSE; z = (x < y) ? x : y; return js_NewNumberValue(cx, z, rval); @@ -218,9 +220,9 @@ math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, y, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; - if (!JS_ValueToNumber(cx, argv[1], &y)) + if (!js_ValueToNumber(cx, argv[1], &y)) return JS_FALSE; z = pow(x, y); return js_NewNumberValue(cx, z, rval); @@ -230,9 +232,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, int64 seed) +random_setSeed(JSRuntime *rt, PRInt64 seed) { - int64 tmp; + PRInt64 tmp; LL_I2L(tmp, 1000); LL_DIV(seed, seed, tmp); @@ -243,7 +245,7 @@ random_setSeed(JSRuntime *rt, int64 seed) static void random_init(JSRuntime *rt) { - int64 tmp, tmp2; + PRInt64 tmp, tmp2; /* Do at most once. */ if (rt->rngInitialized) @@ -274,7 +276,7 @@ random_init(JSRuntime *rt) static uint32 random_next(JSRuntime *rt, int bits) { - int64 nextseed, tmp; + PRInt64 nextseed, tmp; uint32 retval; LL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier); @@ -289,7 +291,7 @@ random_next(JSRuntime *rt, int bits) static jsdouble random_nextDouble(JSRuntime *rt) { - int64 tmp, tmp2; + PRInt64 tmp, tmp2; jsdouble d; LL_ISHL(tmp, random_next(rt, 27), 27); @@ -306,8 +308,10 @@ math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jsdouble z; rt = cx->runtime; + JS_LOCK_RUNTIME(rt); random_init(rt); z = random_nextDouble(rt); + JS_UNLOCK_RUNTIME(rt); return js_NewNumberValue(cx, z, rval); } @@ -316,7 +320,7 @@ math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = floor(x + 0.5); return js_NewNumberValue(cx, z, rval); @@ -327,7 +331,7 @@ math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = sin(x); return js_NewNumberValue(cx, z, rval); @@ -338,7 +342,7 @@ math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = sqrt(x); return js_NewNumberValue(cx, z, rval); @@ -349,7 +353,7 @@ math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = tan(x); return js_NewNumberValue(cx, z, rval); @@ -377,22 +381,33 @@ static JSFunctionSpec math_static_methods[] = { {0} }; +#if JS_HAS_TOSOURCE static JSBool -Math(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) { + *rval = ATOM_KEY(cx->runtime->atomState.MathAtom); return JS_TRUE; } +static JSFunctionSpec math_methods[] = { + {js_toSource_str, math_toSource, 0}, + {0} +}; +#else +#define math_methods NULL +#endif + JSObject * js_InitMathClass(JSContext *cx, JSObject *obj) { - JSObject *proto, *ctor; + JSObject *proto; - proto = JS_InitClass(cx, obj, NULL, &math_class, Math, 0, - NULL, NULL, NULL, math_static_methods); - if (!proto || !(ctor = JS_GetConstructor(cx, proto))) + proto = JS_InitClass(cx, obj, NULL, &math_class, NULL, 0, + NULL, math_methods, NULL, math_static_methods); + if (!proto) return NULL; - if (!JS_DefineConstDoubles(cx, ctor, math_constants)) + if (!JS_DefineConstDoubles(cx, proto, math_constants)) return NULL; return proto; } diff --git a/mozilla/js/src/jsnum.c b/mozilla/js/src/jsnum.c index a87642f4325..981037e8224 100644 --- a/mozilla/js/src/jsnum.c +++ b/mozilla/js/src/jsnum.c @@ -27,6 +27,7 @@ #include #include #include "prtypes.h" +#include "prlog.h" #include "prdtoa.h" #include "prprf.h" #include "jsapi.h" @@ -34,7 +35,7 @@ #include "jscntxt.h" #include "jsconfig.h" #include "jsgc.h" -#include "jslock.h" +#include "jsinterp.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" @@ -56,7 +57,7 @@ num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x)); return JS_TRUE; @@ -67,7 +68,7 @@ num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x; - if (!JS_ValueToNumber(cx, argv[0], &x)) + if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x)); return JS_TRUE; @@ -81,7 +82,7 @@ num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsdouble d, *dp; jschar *ep; - str = JS_ValueToString(cx, argv[0]); + str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; if (!js_strtod(str->chars, &ep, &d) || ep == str->chars) { @@ -99,21 +100,20 @@ static JSBool num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; - jsdouble d; - jsint radix; + jsint base; jschar *ep; + jsdouble d; - str = JS_ValueToString(cx, argv[0]); + str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; if (argc > 1) { - if (!JS_ValueToNumber(cx, argv[1], &d)) + if (!js_ValueToECMAInt32(cx, argv[1], &base)) return JS_FALSE; - radix = (jsint)d; } else { - radix = 0; + base = 0; } - if (!js_strtol(str->chars, &ep, radix, &d)) { + if (!js_strtol(str->chars, &ep, base, &d)) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); } else { if (!js_NewNumberValue(cx, d, rval)) @@ -144,53 +144,85 @@ Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jsval v; if (argc != 0) { - if (!JS_ValueToNumber(cx, argv[0], &d)) + if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; } else { d = 0.0; } if (!js_NewNumberValue(cx, d, &v)) return JS_FALSE; - if (obj->map->clasp != &number_class) { + if (!cx->fp->constructing) { *rval = v; return JS_TRUE; } - if (!js_SetSlot(cx, obj, JSSLOT_PRIVATE, v)) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); return JS_TRUE; } +#if JS_HAS_TOSOURCE +static JSBool +num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + jsdouble d; + size_t i; + char buf[64]; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &number_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_NUMBER(v)) + return js_obj_toSource(cx, obj, argc, argv, rval); + d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); + i = PR_snprintf(buf, sizeof buf, "(new %s(", number_class.name); + + PR_cnvtf(buf + i, sizeof buf - i, 20, d); + i = strlen(buf); + PR_snprintf(buf + i, sizeof buf - i, "))"); + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + static JSBool num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; - jsdouble d, radix; + jsdouble d; jsint base, ival, dval; char *bp, buf[32]; JSString *str; if (!JS_InstanceOf(cx, obj, &number_class, argv)) return JS_FALSE; - JS_LOCK_VOID(cx, v = js_GetSlot(cx, obj, JSSLOT_PRIVATE)); + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_NUMBER(v)) + return js_obj_toString(cx, obj, argc, argv, rval); d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); if (argc != 0) { - if (!JS_ValueToNumber(cx, argv[0], &radix)) + if (!js_ValueToECMAInt32(cx, argv[0], &base)) return JS_FALSE; - base = (jsint)radix; if (base < 2 || base > 36) { JS_ReportError(cx, "illegal radix %d", base); return JS_FALSE; } - ival = (jsint) d; - bp = buf + sizeof buf; - for (*--bp = '\0'; ival != 0 && --bp >= buf; ival /= base) { - dval = ival % base; - *bp = (char)((dval >= 10) ? 'a' - 10 + dval : '0' + dval); + if (base != 10 && JSDOUBLE_IS_FINITE(d)) { + ival = (jsint) js_DoubleToInteger(d); + bp = buf + sizeof buf; + for (*--bp = '\0'; ival != 0 && --bp >= buf; ival /= base) { + dval = ival % base; + *bp = (char)((dval >= 10) ? 'a' - 10 + dval : '0' + dval); + } + if (*bp == '\0') + *--bp = '0'; + str = JS_NewStringCopyZ(cx, bp); + } else { + str = js_NumberToString(cx, d); } - if (*bp == '\0') - *--bp = '0'; - str = JS_NewStringCopyZ(cx, bp); } else { str = js_NumberToString(cx, d); } @@ -205,11 +237,14 @@ num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (!JS_InstanceOf(cx, obj, &number_class, argv)) return JS_FALSE; - JS_LOCK_VOID(cx, *rval = js_GetSlot(cx, obj, JSSLOT_PRIVATE)); + *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; } static JSFunctionSpec number_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, num_toSource, 0}, +#endif {js_toString_str, num_toString, 0}, {js_valueOf_str, num_valueOf, 0}, {0} @@ -266,7 +301,7 @@ js_InitNumberClass(JSContext *cx, JSObject *obj) u.s.lo = 0xffffffff; number_constants[NC_NaN].dval = NaN = u.d; rt->jsNaN = js_NewDouble(cx, NaN); - if (!rt->jsNaN || !JS_LockGCThing(cx, rt->jsNaN)) + if (!rt->jsNaN || !js_LockGCThing(cx, rt->jsNaN)) return NULL; u.s.hi = JSDOUBLE_HI32_EXPMASK; @@ -274,7 +309,7 @@ js_InitNumberClass(JSContext *cx, JSObject *obj) number_constants[NC_POSITIVE_INFINITY].dval = u.d; rt->jsPositiveInfinity = js_NewDouble(cx, u.d); if (!rt->jsPositiveInfinity || - !JS_LockGCThing(cx, rt->jsPositiveInfinity)) { + !js_LockGCThing(cx, rt->jsPositiveInfinity)) { return NULL; } @@ -283,7 +318,7 @@ js_InitNumberClass(JSContext *cx, JSObject *obj) number_constants[NC_NEGATIVE_INFINITY].dval = u.d; rt->jsNegativeInfinity = js_NewDouble(cx, u.d); if (!rt->jsNegativeInfinity || - !JS_LockGCThing(cx, rt->jsNegativeInfinity)) { + !js_LockGCThing(cx, rt->jsNegativeInfinity)) { return NULL; } @@ -299,8 +334,7 @@ js_InitNumberClass(JSContext *cx, JSObject *obj) NULL, number_methods, NULL, NULL); if (!proto || !(ctor = JS_GetConstructor(cx, proto))) return NULL; - if (!js_SetSlot(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO)) - return NULL; + OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO); if (!JS_DefineConstDoubles(cx, ctor, number_constants)) return NULL; @@ -377,10 +411,7 @@ js_NumberToObject(JSContext *cx, jsdouble d) cx->newborn[GCX_OBJECT] = NULL; return NULL; } - if (!js_SetSlot(cx, obj, JSSLOT_PRIVATE, v)) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); return obj; } @@ -395,8 +426,7 @@ js_NumberToString(JSContext *cx, jsdouble d) if (JSDOUBLE_IS_INT(d, i)) { PR_snprintf(buf, sizeof buf, "%ld", (long)i); } else { - /* XXX lock here because prdtoa.c is not threadsafe yet */ - JS_LOCK_VOID(cx, PR_cnvtf(buf, sizeof buf, 20, d)); + PR_cnvtf(buf, sizeof buf, 20, d); } return JS_NewStringCopyZ(cx, buf); } @@ -415,7 +445,7 @@ js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) *dp = 0; return JS_TRUE; } - if (!obj->map->clasp->convert(cx, obj, JSTYPE_NUMBER, &v)) + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v)) return JS_FALSE; } if (JSVAL_IS_INT(v)) { @@ -425,14 +455,16 @@ 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) + if ((!js_strtod(str->chars, &ep, &d) || *ep != 0) && + (!js_strtol(str->chars, &ep, 0, &d) || *ep != 0)) { goto badstr; + } *dp = d; } else if (JSVAL_IS_BOOLEAN(v)) { *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0; } else { #if JS_BUG_FALLIBLE_TONUM - str = js_ValueToSource(cx, v); + str = js_DecompileValueGenerator(cx, v, NULL); badstr: if (str) { JS_ReportError(cx, "%s is not a number", @@ -447,6 +479,67 @@ badstr: return JS_TRUE; } +JSBool +js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) +{ + jsdouble d; + + if (!js_ValueToNumber(cx, v, &d)) + return JS_FALSE; + return js_DoubleToECMAInt32(cx, d, ip); +} + +JSBool +js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip) +{ + jsdouble two32 = 4294967296.0; + jsdouble two31 = 2147483648.0; + + if (!JSDOUBLE_IS_FINITE(d) || d == 0) { + *ip = 0; + return JS_TRUE; + } + d = fmod(d, two32); + d = d >= 0 ? d : d + two32; + if (d >= two31) + *ip = (int32)(d - two32); + else + *ip = (int32)d; + return JS_TRUE; +} + +JSBool +js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) +{ + jsdouble d; + + if (!js_ValueToNumber(cx, v, &d)) + return JS_FALSE; + return js_DoubleToECMAUint32(cx, d, ip); +} + +JSBool +js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip) +{ + JSBool neg; + jsdouble two32 = 4294967296.0; + + if (!JSDOUBLE_IS_FINITE(d) || d == 0) { + *ip = 0; + return JS_TRUE; + } + + neg = (d < 0); + d = floor(neg ? -d : d); + d = neg ? -d : d; + + d = fmod(d, two32); + + d = d >= 0 ? d : d + two32; + *ip = (uint32)d; + return JS_TRUE; +} + JSBool js_ValueToInt32(JSContext *cx, jsval v, int32 *ip) { @@ -456,7 +549,7 @@ js_ValueToInt32(JSContext *cx, jsval v, int32 *ip) if (!js_ValueToNumber(cx, v, &d)) return JS_FALSE; if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) { - str = js_ValueToSource(cx, v); + str = js_DecompileValueGenerator(cx, v, NULL); if (str) { JS_ReportError(cx, "can't convert %s to an integer", JS_GetStringBytes(str)); @@ -544,7 +637,7 @@ js_strtod(const jschar *s, jschar **ep, jsdouble *dp) /* XXXbe rewrite me! */ JSBool -js_strtol(const jschar *s, jschar **ep, jsint radix, jsdouble *dp) +js_strtol(const jschar *s, jschar **ep, jsint base, jsdouble *dp) { size_t i, n; char *cstr, *estr; @@ -562,7 +655,7 @@ js_strtol(const jschar *s, jschar **ep, jsint radix, jsdouble *dp) cstr[i] = (char)s[i]; } errno = 0; - l = strtol(cstr, &estr, radix); + l = strtol(cstr, &estr, base); free(cstr); if (errno == ERANGE) return JS_FALSE; diff --git a/mozilla/js/src/jsnum.h b/mozilla/js/src/jsnum.h index d50b063089a..6b52d408352 100644 --- a/mozilla/js/src/jsnum.h +++ b/mozilla/js/src/jsnum.h @@ -99,13 +99,37 @@ extern JSBool js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); /* - * Convert a value to a number, then to an int32 if it fits (discarding any - * fractional part, but failing with an error if the double is out of range - * or unordered). + * Convert a value or a double to an int32, according to the ECMA rules + * for ToInt32. + */ +extern JSBool +js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); + +extern JSBool +js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip); + +/* + * Convert a value or a double to a uint32, according to the ECMA rules + * for ToUint32. + */ +extern JSBool +js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); + +extern JSBool +js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip); + +/* + * Convert a value to a number, then to an int32 if it fits by rounding to + * nearest; but failing with an error report if the double is out of range + * or unordered. */ extern JSBool js_ValueToInt32(JSContext *cx, jsval v, int32 *ip); +/* + * Convert a value to a number, then to a uint16 according to the ECMA rules + * for ToUint16. + */ extern JSBool js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); diff --git a/mozilla/js/src/jsobj.c b/mozilla/js/src/jsobj.c index 0628087a8d4..42bb122065d 100644 --- a/mozilla/js/src/jsobj.c +++ b/mozilla/js/src/jsobj.c @@ -45,21 +45,39 @@ #include "jslock.h" #include "jsnum.h" #include "jsobj.h" -#include "jsopcode.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #if JS_HAS_OBJ_WATCHPOINT #include "jsdbgapi.h" - -extern JSProperty * -js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid); - -extern JSBool PR_CALLBACK -js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp); #endif +#ifdef JS_THREADSAFE +#define NATIVE_DROP_PROPERTY js_DropProperty + +extern void +js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); +#else +#define NATIVE_DROP_PROPERTY NULL +#endif + +JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { + js_NewObjectMap, js_DestroyObjectMap, +#if defined JS_THREADSAFE && defined DEBUG + _js_LookupProperty, js_DefineProperty, +#else + js_LookupProperty, js_DefineProperty, +#endif + js_GetProperty, js_SetProperty, + js_GetAttributes, js_SetAttributes, + js_DeleteProperty, js_DefaultValue, + js_Enumerate, js_CheckAccess, + NULL, NATIVE_DROP_PROPERTY, + js_Call, js_Construct, + NULL, js_HasInstance +}; + JSClass js_ObjectClass = { js_Object_str, 0, @@ -89,7 +107,21 @@ static JSPropertySpec object_props[] = { static JSBool obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - JS_LOCK_VOID(cx, *vp = js_GetSlot(cx, obj, JSVAL_TO_INT(id))); + jsint slot; + JSAccessMode mode; + uintN attrs; + + slot = JSVAL_TO_INT(id); + if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { + id = (jsid)cx->runtime->atomState.protoAtom; + mode = JSACC_PROTO; + } else { + id = (jsid)cx->runtime->atomState.parentAtom; + mode = JSACC_PARENT; + } + if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs)) + return JS_FALSE; + *vp = OBJ_GET_SLOT(cx, obj, slot); return JS_TRUE; } @@ -98,41 +130,44 @@ obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSObject *obj2; jsint slot; - JSBool ok; if (!JSVAL_IS_OBJECT(*vp)) return JS_TRUE; obj2 = JSVAL_TO_OBJECT(*vp); slot = JSVAL_TO_INT(id); - JS_LOCK(cx); while (obj2) { if (obj2 == obj) { JS_ReportError(cx, "cyclic %s value", object_props[slot].name); - ok = JS_FALSE; - goto out; + return JS_FALSE; } - obj2 = JSVAL_TO_OBJECT(js_GetSlot(cx, obj2, slot)); + obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot)); } - ok = js_SetSlot(cx, obj, slot, *vp); -out: - JS_UNLOCK(cx); - return ok; + OBJ_SET_SLOT(cx, obj, slot, *vp); + return JS_TRUE; } static JSBool obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - jsint count; - JSProperty *prop; + jsval iter_state; + jsid num_properties; - count = 0; - JS_LOCK(cx); - for (prop = obj->map->props; prop; prop = prop->next) - if (prop->flags & JSPROP_ENUMERATE) - count++; - JS_UNLOCK(cx); - *vp = INT_TO_JSVAL(count); + iter_state = JSVAL_NULL; + + /* Get the number of properties to enumerate. */ + if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties)) + goto error; + if (!JSVAL_IS_INT(num_properties)) { + PR_ASSERT(0); + goto error; + } + *vp = num_properties; return JS_TRUE; + +error: + if (iter_state != JSVAL_NULL) + OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); + return JS_FALSE; } #else /* !JS_HAS_OBJ_PROTO_PROP */ @@ -141,8 +176,6 @@ obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp) #endif /* !JS_HAS_OBJ_PROTO_PROP */ -#if JS_HAS_SHARP_VARS - PR_STATIC_CALLBACK(PRHashNumber) js_hash_object(const void *key) { @@ -150,14 +183,16 @@ js_hash_object(const void *key) } static PRHashEntry * -MarkSharpObjects(JSContext *cx, JSObject *obj) +MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) { JSSharpObjectMap *map; PRHashTable *table; PRHashNumber hash; PRHashEntry **hep, *he; jsatomid sharpid; - JSProperty *prop; + JSIdArray *ida; + JSBool ok; + jsint i, length; jsval val; map = &cx->sharpObjectMap; @@ -172,28 +207,44 @@ MarkSharpObjects(JSContext *cx, JSObject *obj) JS_ReportOutOfMemory(cx); return NULL; } - for (prop = obj->map->props; prop; prop = prop->next) { - if (!prop->symbols || !(prop->flags & JSPROP_ENUMERATE)) - continue; - val = prop->object->slots[prop->slot]; - if (JSVAL_IS_OBJECT(val) && val != JSVAL_NULL) - (void) MarkSharpObjects(cx, JSVAL_TO_OBJECT(val)); + ida = JS_Enumerate(cx, obj); + if (!ida) + return NULL; + ok = JS_TRUE; + for (i = 0, length = ida->length; i < length; i++) { + ok = OBJ_GET_PROPERTY(cx, obj, ida->vector[i], &val); + if (!ok) + break; + if (!JSVAL_IS_PRIMITIVE(val) && + !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) { + ok = JS_FALSE; + break; + } } + if (!ok || !idap) + JS_DestroyIdArray(cx, ida); + if (!ok) + return NULL; } else { sharpid = (jsatomid) he->value; if (sharpid == 0) { sharpid = ++map->sharpgen << 1; he->value = (void *) sharpid; } + ida = NULL; } + if (idap) + *idap = ida; return he; } PRHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, jschar **sp) +js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, + jschar **sp) { JSSharpObjectMap *map; PRHashTable *table; + JSIdArray *ida; PRHashNumber hash; PRHashEntry *he; jsatomid sharpid; @@ -212,10 +263,16 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, jschar **sp) map->table = table; } + ida = NULL; if (map->depth == 0) { - he = MarkSharpObjects(cx, obj); + he = MarkSharpObjects(cx, obj, &ida); if (!he) return NULL; + PR_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0); + if (!idap) { + JS_DestroyIdArray(cx, ida); + ida = NULL; + } } else { hash = js_hash_object(obj); he = *PR_HashTableRawLookup(table, hash, obj); @@ -229,19 +286,36 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, jschar **sp) len = PR_snprintf(buf, sizeof buf, "#%u%c", sharpid >> 1, (sharpid & SHARP_BIT) ? '#' : '='); *sp = js_InflateString(cx, buf, len); - if (!*sp) + if (!*sp) { + if (ida) + JS_DestroyIdArray(cx, ida); return NULL; + } } - if ((sharpid & SHARP_BIT) == 0) + if ((sharpid & SHARP_BIT) == 0) { + if (idap && !ida) { + ida = JS_Enumerate(cx, obj); + if (!ida) { + if (*sp) { + JS_free(cx, *sp); + *sp = NULL; + } + return NULL; + } + } map->depth++; + } + if (idap) + *idap = ida; return he; } void -js_LeaveSharpObject(JSContext *cx) +js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) { JSSharpObjectMap *map; + JSIdArray *ida; map = &cx->sharpObjectMap; PR_ASSERT(map->depth > 0); @@ -250,11 +324,189 @@ js_LeaveSharpObject(JSContext *cx) PR_HashTableDestroy(map->table); map->table = NULL; } + if (idap) { + ida = *idap; + if (ida) { + JS_DestroyIdArray(cx, ida); + *idap = NULL; + } + } } -#endif /* JS_HAS_SHARP_VARS */ +#define OBJ_TOSTRING_EXTRA 2 /* for 2 GC roots */ -#define OBJ_TOSTRING_NARGS 2 /* for 2 GC roots */ +#if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE +JSBool +js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + PRHashEntry *he; + JSIdArray *ida; + jschar *chars, *ochars, *vchars, *vsharp; + size_t nchars, vlength, vsharplength; + JSBool ok; + char *comma; + jsint i, length; + jsid id; + jsval val; + JSString *idstr, *valstr, *str; + + he = js_EnterSharpObject(cx, obj, &ida, &chars); + if (!he) + return JS_FALSE; + if (IS_SHARP(he)) { /* we didn't enter -- obj is already sharp */ + PR_ASSERT(!ida); +#if JS_HAS_SHARP_VARS + nchars = js_strlen(chars); +#else + chars[0] = '{'; + chars[1] = '}'; + chars[2] = 0; + nchars = 2; +#endif + goto make_string; + } + PR_ASSERT(ida); + ok = JS_TRUE; + + /* Allocate 2 + 1 for "{}" and the terminator. */ + if (!chars) { + chars = malloc((2 + 1) * sizeof(jschar)); + if (!chars) + goto done; + nchars = 0; + } else { + MAKE_SHARP(he); + nchars = js_strlen(chars); + chars = realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); + if (!chars) { + free(ochars); + goto done; + } + } + chars[nchars++] = '{'; + + comma = NULL; + + for (i = 0, length = ida->length; i < length; i++) { + /* Get strings for id and val and GC-root them via argv. */ + id = ida->vector[i]; + ok = OBJ_GET_PROPERTY(cx, obj, id, &val); + if (!ok) + goto done; + + /* Convert id to a jsval and then to a string. */ + id = js_IdToValue(id); + idstr = js_ValueToString(cx, id); + if (!idstr) { + ok = JS_FALSE; + goto done; + } + argv[0] = STRING_TO_JSVAL(idstr); + + /* If id is a non-identifier string, it needs to be quoted. */ + if (JSVAL_IS_STRING(id) && !js_IsIdentifier(idstr)) { + idstr = js_QuoteString(cx, idstr, '\''); + if (!idstr) { + ok = JS_FALSE; + goto done; + } + argv[0] = STRING_TO_JSVAL(idstr); + } + + /* Convert val to its canonical source form. */ + valstr = js_ValueToSource(cx, val); + if (!valstr) { + ok = JS_FALSE; + goto done; + } + argv[1] = STRING_TO_JSVAL(valstr); + vchars = valstr->chars; + vlength = valstr->length; + + /* If val is a non-sharp object, consider sharpening it. */ + vsharp = NULL; + vsharplength = 0; +#if JS_HAS_SHARP_VARS + if (!JSVAL_IS_PRIMITIVE(val) && vchars[0] != '#') { + he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val), NULL, &vsharp); + if (!he) { + ok = JS_FALSE; + goto done; + } + if (IS_SHARP(he)) { + vchars = vsharp; + vlength = js_strlen(vchars); + } else { + if (vsharp) { + vsharplength = js_strlen(vsharp); + MAKE_SHARP(he); + } + js_LeaveSharpObject(cx, NULL); + } + } +#endif + + /* Allocate 1 + 1 at end for closing brace and terminating 0. */ + chars = realloc((ochars = chars), + (nchars + (comma ? 2 : 0) + + idstr->length + 1 + vsharplength + vlength + + 1 + 1) * sizeof(jschar)); + if (!chars) { + /* Save code space on error: let JS_free ignore null vsharp. */ + JS_free(cx, vsharp); + free(ochars); + goto done; + } + + if (comma) { + chars[nchars++] = comma[0]; + chars[nchars++] = comma[1]; + } + comma = ", "; + + js_strncpy(&chars[nchars], idstr->chars, idstr->length); + nchars += idstr->length; + chars[nchars++] = ':'; + + if (vsharplength) { + js_strncpy(&chars[nchars], vsharp, vsharplength); + nchars += vsharplength; + } + js_strncpy(&chars[nchars], vchars, vlength); + nchars += vlength; + + if (vsharp) + JS_free(cx, vsharp); + } + + done: + if (chars) { + chars[nchars++] = '}'; + chars[nchars] = 0; + } + js_LeaveSharpObject(cx, &ida); + + if (!ok) { + if (chars) + free(chars); + return ok; + } + + if (!chars) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + make_string: + str = js_NewString(cx, chars, nchars, 0); + if (!str) { + free(chars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */ JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, @@ -265,175 +517,29 @@ js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, char *clazz, *prefix; JSString *str; -#if JS_HAS_OBJECT_LITERAL - if (cx->version >= JSVERSION_1_2) { - JSProperty *list, *prop; - JSBool ok; - char *comma, *idquote, *valquote; - jsval id, val; - JSString *idstr, *valstr; -#if JS_HAS_SHARP_VARS - PRHashEntry *he; +#if JS_HAS_INITIALIZERS + if (cx->version == JSVERSION_1_2) + return js_obj_toSource(cx, obj, argc, argv, rval); #endif - /* Early returns after this must unlock, or goto done with ok set. */ - JS_LOCK(cx); - list = obj->map->props; - -#if JS_HAS_SHARP_VARS - he = js_EnterSharpObject(cx, obj, &chars); - if (!he) { - JS_UNLOCK(cx); - return JS_FALSE; - } - if (IS_SHARP(he)) { /* we didn't enter -- obj is already sharp */ - JS_UNLOCK(cx); - nchars = js_strlen(chars); - goto make_string; - } - MAKE_SHARP(he); -#else - /* Shut off recursion by temporarily clearing obj's property list. */ - obj->map->props = NULL; - chars = NULL; -#endif - ok = JS_TRUE; - - /* Allocate 2 + 1 for "{}" and the terminator. */ - if (!chars) { - chars = malloc((2 + 1) * sizeof(jschar)); - nchars = 0; - } else { - nchars = js_strlen(chars); - chars = realloc(chars, (nchars + 2 + 1) * sizeof(jschar)); - } - if (!chars) - goto done; - chars[nchars++] = '{'; - - comma = NULL; - - for (prop = list; prop; prop = prop->next) { - if (!prop->symbols || !(prop->flags & JSPROP_ENUMERATE)) - continue; - - /* Get strings for id and val and GC-root them via argv. */ - id = js_IdToValue(sym_id(prop->symbols)); - idstr = js_ValueToString(cx, id); - if (idstr) - argv[0] = STRING_TO_JSVAL(idstr); - val = prop->object->slots[prop->slot]; - valstr = js_ValueToString(cx, val); - if (!idstr || !valstr) { - ok = JS_FALSE; - goto done; - } - argv[1] = STRING_TO_JSVAL(valstr); - - /* If id is a non-identifier string, it needs to be quoted. */ - if (JSVAL_IS_STRING(id) && !js_IsIdentifier(idstr)) { - idquote = "'"; - idstr = js_EscapeString(cx, idstr, *idquote); - if (!idstr) { - ok = JS_FALSE; - goto done; - } - argv[0] = STRING_TO_JSVAL(idstr); - } else { - idquote = NULL; - } - - /* Same for val, except it can't be an identifier. */ - if (JSVAL_IS_STRING(val)) { - valquote = "\""; - valstr = js_EscapeString(cx, valstr, *valquote); - if (!valstr) { - ok = JS_FALSE; - goto done; - } - argv[1] = STRING_TO_JSVAL(valstr); - } else { - valquote = NULL; - } - - /* Allocate 1 + 1 at end for closing brace and terminating 0. */ - chars = realloc(chars, - (nchars + (comma ? 2 : 0) + - (idquote ? 2 : 0) + idstr->length + 1 + - (valquote ? 2 : 0) + valstr->length + - 1 + 1) * sizeof(jschar)); - if (!chars) - goto done; - - if (comma) { - chars[nchars++] = comma[0]; - chars[nchars++] = comma[1]; - } - comma = ", "; - - if (idquote) - chars[nchars++] = *idquote; - js_strncpy(&chars[nchars], idstr->chars, idstr->length); - nchars += idstr->length; - if (idquote) - chars[nchars++] = *idquote; - - chars[nchars++] = ':'; - - if (valquote) - chars[nchars++] = *valquote; - js_strncpy(&chars[nchars], valstr->chars, valstr->length); - nchars += valstr->length; - if (valquote) - chars[nchars++] = *valquote; - } - - done: - if (chars) { - chars[nchars++] = '}'; - chars[nchars] = 0; - } -#if JS_HAS_SHARP_VARS - js_LeaveSharpObject(cx); -#else - /* Restore obj's property list before bailing on error. */ - obj->map->props = list; -#endif - - JS_UNLOCK(cx); - if (!ok) { - if (chars) - free(chars); - return ok; - } - } else -#endif /* JS_HAS_OBJECT_LITERAL */ - { - clazz = obj->map->clasp->name; - nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ - chars = malloc((nchars + 1) * sizeof(jschar)); - if (chars) { - prefix = "[object "; - nchars = 0; - while ((chars[nchars] = (jschar)*prefix) != 0) - nchars++, prefix++; - while ((chars[nchars] = (jschar)*clazz) != 0) - nchars++, clazz++; - chars[nchars++] = ']'; - chars[nchars] = 0; - } - } - -#if JS_HAS_SHARP_VARS - make_string: -#endif - if (!chars) { - JS_ReportOutOfMemory(cx); + clazz = OBJ_GET_CLASS(cx, obj)->name; + nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ + chars = JS_malloc(cx, (nchars + 1) * sizeof(jschar)); + if (!chars) return JS_FALSE; - } + + prefix = "[object "; + nchars = 0; + while ((chars[nchars] = (jschar)*prefix) != 0) + nchars++, prefix++; + while ((chars[nchars] = (jschar)*clazz) != 0) + nchars++, clazz++; + chars[nchars++] = ']'; + chars[nchars] = 0; + str = js_NewString(cx, chars, nchars, 0); if (!str) { - free(chars); + JS_free(cx, chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); @@ -450,13 +556,14 @@ obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSStackFrame *fp, *caller; - JSBool ok; + JSStackFrame *caller; + JSObject *scopeobj; JSString *str; const char *file; uintN line; JSPrincipals *principals; JSScript *script; + JSBool ok; #if JS_HAS_EVAL_THIS_SCOPE JSObject *callerScopeChain; JSBool implicitWith; @@ -467,34 +574,45 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_TRUE; } - fp = cx->fp; - caller = fp->down; -#if !JS_BUG_EVAL_THIS_FUN - /* Ensure that this flows into eval from the calling function, if any. */ - fp->thisp = caller->thisp; -#endif -#if JS_HAS_SHARP_VARS - fp->sharpArray = caller->sharpArray; -#endif + caller = cx->fp->down; -#if JS_HAS_EVAL_THIS_SCOPE - /* If obj.eval(str), emulate 'with (obj) eval(str)' in the calling frame. */ - callerScopeChain = caller->scopeChain; - implicitWith = (callerScopeChain != obj && - (callerScopeChain->map->clasp != &js_WithClass || - OBJ_GET_PROTO(callerScopeChain) != obj)); - if (implicitWith) { - obj = js_NewObject(cx, &js_WithClass, obj, callerScopeChain); - if (!obj) +#if JS_HAS_SCRIPT_OBJECT + /* + * Script.prototype.compile/exec and Object.prototype.eval all take an + * optional trailing argument that overrides the scope object. + */ + scopeobj = NULL; + if (argc >= 2) { + if (!js_ValueToObject(cx, argv[1], &scopeobj)) return JS_FALSE; - caller->scopeChain = obj; + argv[1] = OBJECT_TO_JSVAL(scopeobj); } + if (!scopeobj) +#endif + { +#if JS_HAS_EVAL_THIS_SCOPE + /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ + callerScopeChain = caller->scopeChain; + implicitWith = (callerScopeChain != obj && + (OBJ_GET_CLASS(cx, callerScopeChain) != &js_WithClass || + OBJ_GET_PROTO(cx, callerScopeChain) != obj)); + if (implicitWith) { + scopeobj = js_NewObject(cx, &js_WithClass, obj, callerScopeChain); + if (!scopeobj) + return JS_FALSE; + caller->scopeChain = scopeobj; + } + /* From here on, control must exit through label out with ok set. */ #endif -#if !JS_BUG_EVAL_THIS_SCOPE - /* Compile using caller's current scope object (might be a function). */ - obj = caller->scopeChain; +#if JS_BUG_EVAL_THIS_SCOPE + /* An old version used the object in which eval was found for scope. */ + scopeobj = obj; +#else + /* Compile using caller's current scope object (might be a function). */ + scopeobj = caller->scopeChain; #endif + } str = JSVAL_TO_STRING(argv[0]); if (caller->script) { @@ -506,7 +624,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) line = 0; principals = NULL; } - script = JS_CompileUCScriptForPrincipals(cx, obj, principals, + script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, str->chars, str->length, file, line); if (!script) { @@ -514,19 +632,23 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) goto out; } -#if !JS_BUG_EVAL_THIS_SCOPE - /* Interpret using caller's new scope object (might be a Call object). */ - obj = caller->scopeChain; +#if JS_HAS_SCRIPT_OBJECT + if (argc < 2) #endif - ok = js_Execute(cx, obj, script, fp, rval); +#if !JS_BUG_EVAL_THIS_SCOPE + { + /* Execute using caller's new scope object (might be a Call object). */ + scopeobj = caller->scopeChain; + } +#endif + ok = js_Execute(cx, scopeobj, script, caller->fun, caller, JS_FALSE, rval); JS_DestroyScript(cx, script); out: #if JS_HAS_EVAL_THIS_SCOPE - if (implicitWith) { - /* Restore OBJ_GET_PARENT(obj) not callerScopeChain in case of Call. */ - caller->scopeChain = OBJ_GET_PARENT(obj); - } + /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ + if (implicitWith) + caller->scopeChain = OBJ_GET_PARENT(cx, scopeobj); #endif return ok; } @@ -540,103 +662,62 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, JSObject *funobj; jsval argv[3]; - PR_ASSERT(JS_IS_LOCKED(cx)); funobj = closure; argv[0] = id; argv[1] = old; argv[2] = *nvp; - return js_Call(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp); + return js_CallFunctionValue(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp); } static JSBool obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFunction *fun; - jsval userid, symid, propid, value; + jsval userid, value; + jsid symid; JSAtom *atom; - JSRuntime *rt; - JSBool ok; - JSProperty *prop; - JSPropertyOp getter, setter; - JSClass *clasp; + uintN attrs; - fun = JS_ValueToFunction(cx, argv[1]); + fun = js_ValueToFunction(cx, &argv[1], JS_FALSE); if (!fun) return JS_FALSE; argv[1] = OBJECT_TO_JSVAL(fun->object); - /* - * Lock the world while we look in obj. Be sure to return via goto out - * on error, so we unlock. - */ - rt = cx->runtime; - JS_LOCK_RUNTIME(rt); - /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ userid = argv[0]; if (JSVAL_IS_INT(userid)) { - symid = userid; + symid = (jsid)userid; atom = NULL; } else { atom = js_ValueToStringAtom(cx, userid); - if (!atom) { - ok = JS_FALSE; - goto out; - } - symid = (jsval)atom; + if (!atom) + return JS_FALSE; + symid = (jsid)atom; } - ok = js_LookupProperty(cx, obj, symid, &obj, &prop); - if (atom) - js_DropAtom(cx, atom); - if (!ok) - goto out; - - /* Set propid from the property, in case it has a tinyid. */ - if (prop) { - propid = prop->id; - getter = prop->getter; - setter = prop->setter; - value = prop->object->slots[prop->slot]; - } else { - propid = userid; - clasp = obj->map->clasp; - getter = clasp->getProperty; - setter = clasp->setProperty; - value = JSVAL_VOID; - } - - /* - * Security policy is implemented in getters and setters, so we have to - * call get and set here to let the JS API client check for a watchpoint - * that crosses a trust boundary. - * XXX assumes get and set are idempotent, should use a clasp->watch hook - */ - ok = getter(cx, obj, propid, &value) && setter(cx, obj, propid, &value); - if (!ok) - goto out; - - /* Finally, call into jsdbgapi.c to set the watchpoint on userid. */ - ok = JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object); - -out: - JS_UNLOCK_RUNTIME(rt); - return ok; + if (!OBJ_CHECK_ACCESS(cx, obj, symid, JSACC_WATCH, &value, &attrs)) + return JS_FALSE; + if (attrs & JSPROP_READONLY) + return JS_TRUE; + return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object); } static JSBool obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JS_LOCK_VOID(cx, JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL)); + JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL); return JS_TRUE; } #endif /* JS_HAS_OBJ_WATCHPOINT */ static JSFunctionSpec object_methods[] = { - {js_toString_str, js_obj_toString, OBJ_TOSTRING_NARGS}, +#if JS_HAS_TOSOURCE + {js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA}, +#endif + {js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA}, {js_valueOf_str, obj_valueOf, 0}, - {js_eval_str, obj_eval, 1}, + {js_eval_str, obj_eval, 1, 0, 1}, #if JS_HAS_OBJ_WATCHPOINT {"watch", obj_watch, 2}, {"unwatch", obj_unwatch, 1}, @@ -647,33 +728,150 @@ static JSFunctionSpec object_methods[] = { static JSBool Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - if (argc != 0) { - if (!JS_ValueToObject(cx, argv[0], &obj)) + if (argc == 0) { + /* Trigger logic below to construct a blank object. */ + obj = NULL; + } else { + /* If argv[0] is null or undefined, obj comes back null. */ + if (!js_ValueToObject(cx, argv[0], &obj)) return JS_FALSE; - if (!obj) + } + if (!obj) { + PR_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); + if (cx->fp->constructing) return JS_TRUE; + obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + if (!obj) + return JS_FALSE; } *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } /* - * Class for with-statement stack objects. + * ObjectOps and Class for with-statement stack objects. */ static JSBool -with_resolve(JSContext *cx, JSObject *obj, jsval id, JSObject **objp) +with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp +#if defined JS_THREADSAFE && defined DEBUG + , const char *file, uintN line +#endif + ) { - *objp = OBJ_GET_PROTO(obj); - return JS_TRUE; + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_LookupProperty(cx, obj, id, objp, propp); + return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); +} + +static JSBool +with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_GetProperty(cx, obj, id, vp); + return OBJ_GET_PROPERTY(cx, proto, id, vp); +} + +static JSBool +with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_SetProperty(cx, obj, id, vp); + return OBJ_SET_PROPERTY(cx, proto, id, vp); +} + +static JSBool +with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_GetAttributes(cx, obj, id, prop, attrsp); + return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp); +} + +static JSBool +with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_SetAttributes(cx, obj, id, prop, attrsp); + return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp); +} + +static JSBool +with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_DeleteProperty(cx, obj, id, rval); + return OBJ_DELETE_PROPERTY(cx, proto, id, rval); +} + +static JSBool +with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_DefaultValue(cx, obj, hint, vp); + return OBJ_DEFAULT_VALUE(cx, proto, hint, vp); +} + +static JSBool +with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_Enumerate(cx, obj, enum_op, statep, idp); + return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp); +} + +static JSBool +with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_CheckAccess(cx, obj, id, mode, vp, attrsp); + return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp); +} + +static JSObject * +with_ThisObject(JSContext *cx, JSObject *obj) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return obj; + return OBJ_THIS_OBJECT(cx, proto); +} + +static JSObjectOps with_object_ops = { + js_NewObjectMap, js_DestroyObjectMap, + with_LookupProperty, js_DefineProperty, + with_GetProperty, with_SetProperty, + with_GetAttributes, with_SetAttributes, + with_DeleteProperty, with_DefaultValue, + with_Enumerate, with_CheckAccess, + with_ThisObject, NATIVE_DROP_PROPERTY +}; + +static JSObjectOps * +with_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &with_object_ops; } JSClass js_WithClass = { "With", - JSCLASS_NEW_RESOLVE, - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, (JSResolveOp)with_resolve, - JS_ConvertStub, JS_FinalizeStub + 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + with_getObjectOps }; #if JS_HAS_OBJ_PROTO_PROP @@ -683,18 +881,22 @@ With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSObject *parent, *proto; jsval v; - if (!JS_InstanceOf(cx, obj, &js_WithClass, argv)) - return JS_FALSE; + if (!cx->fp->constructing) { + obj = js_NewObject(cx, &js_WithClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } parent = cx->fp->scopeChain; if (argc > 0) { - if (!JS_ValueToObject(cx, argv[0], &proto)) + if (!js_ValueToObject(cx, argv[0], &proto)) return JS_FALSE; v = OBJECT_TO_JSVAL(proto); if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v)) return JS_FALSE; if (argc > 1) { - if (!JS_ValueToObject(cx, argv[1], &parent)) + if (!js_ValueToObject(cx, argv[1], &parent)) return JS_FALSE; } } @@ -712,7 +914,7 @@ js_InitObjectClass(JSContext *cx, JSObject *obj) PR_ASSERT(sizeof(jsatomid) * PR_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1); #endif - proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 0, + proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1, object_props, object_methods, NULL, NULL); #if JS_HAS_OBJ_PROTO_PROP if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0, @@ -723,11 +925,59 @@ js_InitObjectClass(JSContext *cx, JSObject *obj) return proto; } +void +js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, + JSClass *clasp) +{ + map->nrefs = nrefs; + map->ops = ops; + map->nslots = 0; + map->freeslot = JSSLOT_FREE(clasp); +} + +JSObjectMap * +js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, + JSClass *clasp, JSObject *obj) +{ + return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj); +} + +void +js_DestroyObjectMap(JSContext *cx, JSObjectMap *map) +{ + js_DestroyScope(cx, (JSScope *)map); +} + +JSObjectMap * +js_HoldObjectMap(JSContext *cx, JSObjectMap *map) +{ + PR_ASSERT(map->nrefs >= 0); + JS_ATOMIC_ADDREF(&map->nrefs, 1); + return map; +} + +JSObjectMap * +js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) +{ + PR_ASSERT(map->nrefs > 0); + JS_ATOMIC_ADDREF(&map->nrefs, -1); + if (map->nrefs == 0) { + map->ops->destroyObjectMap(cx, map); + return NULL; + } + if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj) + ((JSScope *)map)->object = NULL; + return map; +} + JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) { - JSObject *obj; - JSScope *scope; + JSObject *obj, *ctor; + JSObjectOps *ops; + JSObjectMap *map; + jsval cval; + uint32 i; /* Allocate an object from the GC heap and zero it. */ obj = js_AllocGCThing(cx, GCX_OBJECT); @@ -736,55 +986,72 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) /* Bootstrap the ur-object, and make it the default prototype object. */ if (!proto) { - if (!js_GetClassPrototype(cx, clasp, &proto) || - (!proto && !js_GetClassPrototype(cx, &js_ObjectClass, &proto))) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } + if (!js_GetClassPrototype(cx, clasp->name, &proto)) + goto bad; + if (!proto && !js_GetClassPrototype(cx, js_ObjectClass.name, &proto)) + goto bad; } - /* Share the given prototype's scope (create one if necessary). */ - if (proto && proto->map->clasp == clasp) { - scope = (JSScope *)proto->map; - JS_LOCK_VOID(cx, obj->map = (JSObjectMap *)js_HoldScope(cx, scope)); + /* Always call the class's getObjectOps hook if it has one. */ + ops = clasp->getObjectOps + ? clasp->getObjectOps(cx, clasp) + : &js_ObjectOps; + + if (proto && (map = proto->map)->ops == ops) { + /* Default parent to the parent of the prototype's constructor. */ + if (!parent) { + if (!OBJ_GET_PROPERTY(cx, proto, + (jsid)cx->runtime->atomState.constructorAtom, + &cval)) { + goto bad; + } + if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL) + parent = OBJ_GET_PARENT(cx, ctor); + } + + /* Share the given prototype's map. */ + obj->map = js_HoldObjectMap(cx, map); } else { - scope = js_NewScope(cx, clasp, obj); - if (!scope) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } - scope->map.nrefs = 1; - obj->map = &scope->map; + /* Leave parent alone. Allocate a new map for obj. */ + map = ops->newObjectMap(cx, 1, ops, clasp, obj); + if (!map) + goto bad; + if (map->nslots == 0) + map->nslots = JS_INITIAL_NSLOTS; + obj->map = map; } - /* Set the prototype and parent properties. */ - if (!js_SetSlot(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)) || - !js_SetSlot(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent))) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } + /* Set the proto, parent, and class properties. */ + obj->slots = JS_malloc(cx, JS_INITIAL_NSLOTS * sizeof(jsval)); + if (!obj->slots) + goto bad; + obj->slots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); + obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); + obj->slots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp); + for (i = JSSLOT_CLASS+1; i < JS_INITIAL_NSLOTS; i++) + obj->slots[i] = JSVAL_VOID; return obj; + +bad: + cx->newborn[GCX_OBJECT] = NULL; + return NULL; } static JSBool -FindConstructor(JSContext *cx, JSClass *clasp, jsval *vp) +FindConstructor(JSContext *cx, const char *name, jsval *vp) { JSAtom *atom; JSObject *obj, *tmp; - JSBool ok; - PR_ASSERT(JS_IS_LOCKED(cx)); - - /* XXX pre-atomize in JS_InitClass! */ - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); + atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - if (cx->fp && (tmp = cx->fp->scopeChain)) { + if (cx->fp && (tmp = cx->fp->scopeChain) != NULL) { /* Find the topmost object in the scope chain. */ do { obj = tmp; - tmp = OBJ_GET_PARENT(obj); + tmp = OBJ_GET_PARENT(cx, obj); } while (tmp); } else { obj = cx->globalObject; @@ -794,9 +1061,7 @@ FindConstructor(JSContext *cx, JSClass *clasp, jsval *vp) } } - ok = (js_GetProperty(cx, obj, (jsval)atom, vp) != NULL); - js_DropAtom(cx, atom); - return JS_TRUE; + return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); } JSObject * @@ -805,29 +1070,49 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, { JSObject *obj; JSBool ok; - jsval ctor, rval; + jsval cval, *sp, *oldsp, rval; + void *mark; + JSStackFrame *fp; obj = js_NewObject(cx, clasp, proto, parent); if (!obj) return NULL; - JS_LOCK_VOID(cx, ok = FindConstructor(cx, clasp, &ctor)); - if (!ok || !js_Call(cx, obj, ctor, 0, NULL, &rval)) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } + ok = FindConstructor(cx, clasp->name, &cval); + if (!ok) + goto bad; + + /* Allocate stack space for cval and obj, then push them. */ + sp = js_AllocStack(cx, 2, &mark); + if (!sp) + goto bad; + *sp++ = cval; + *sp++ = OBJECT_TO_JSVAL(obj); + + /* Lift current frame to include the args and do the call. */ + fp = cx->fp; + oldsp = fp->sp; + fp->sp = sp; + ok = js_Invoke(cx, 0, JS_TRUE); + rval = sp[-1]; + fp->sp = oldsp; + js_FreeStack(cx, mark); + + if (!ok) + goto bad; return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj; +bad: + cx->newborn[GCX_OBJECT] = NULL; + return NULL; } void js_FinalizeObject(JSContext *cx, JSObject *obj) { - JSScope *scope; + JSObjectMap *map; - PR_ASSERT(JS_IS_LOCKED(cx)); - - /* Cope with stillborn objects that have no scope. */ - scope = (JSScope *)obj->map; - if (!scope) + /* Cope with stillborn objects that have no map. */ + map = obj->map; + if (!map) return; #if JS_HAS_OBJ_WATCHPOINT @@ -836,49 +1121,15 @@ js_FinalizeObject(JSContext *cx, JSObject *obj) #endif /* Finalize obj first, in case it needs map and slots. */ - scope->map.clasp->finalize(cx, obj); - if (scope->object == obj) - scope->object = NULL; + OBJ_GET_CLASS(cx, obj)->finalize(cx, obj); - /* Drop scope and free slots. */ - js_DropScope(cx, scope); + /* Drop map and free slots. */ + js_DropObjectMap(cx, map, obj); obj->map = NULL; JS_free(cx, obj->slots); obj->slots = NULL; } -jsval -js_GetSlot(JSContext *cx, JSObject *obj, uint32 slot) -{ - if (!obj->slots || slot >= obj->map->freeslot) - return JSVAL_NULL; - return obj->slots[slot]; -} - -JSBool -js_SetSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval value) -{ - uint32 i; - - PR_ASSERT(slot < JS_INITIAL_NSLOTS); - if (!obj->slots) { - obj->slots = JS_malloc(cx, JS_INITIAL_NSLOTS * sizeof(jsval)); - if (!obj->slots) - return JS_FALSE; - for (i = 0; i < (uint32)JS_INITIAL_NSLOTS; i++) { - if (i == slot) - continue; - obj->slots[i] = JSVAL_VOID; - } - if (obj->map->nslots == 0) - obj->map->nslots = JS_INITIAL_NSLOTS; - } - if (slot >= obj->map->freeslot) - obj->map->freeslot = slot + 1; - obj->slots[slot] = value; - return JS_TRUE; -} - JSBool js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) { @@ -887,10 +1138,7 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) size_t nbytes; jsval *newslots; - PR_ASSERT(JS_IS_LOCKED(cx)); map = obj->map; - PR_ASSERT(((JSScope *)map)->object == obj); - nslots = map->nslots; if (map->freeslot >= nslots) { nslots = PR_MAX(map->freeslot, nslots); @@ -907,10 +1155,12 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) } #endif - if (obj->slots) + if (obj->slots) { newslots = JS_realloc(cx, obj->slots, nbytes); - else + } else { + /* obj must be newborn and unshared at this point. */ newslots = JS_malloc(cx, nbytes); + } if (!newslots) return JS_FALSE; obj->slots = newslots; @@ -932,9 +1182,8 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) size_t nbytes; jsval *newslots; - PR_ASSERT(JS_IS_LOCKED(cx)); + obj->slots[slot] = JSVAL_VOID; map = obj->map; - PR_ASSERT(((JSScope *)map)->object == obj); if (map->freeslot == slot + 1) map->freeslot = slot; nslots = map->nslots; @@ -972,6 +1221,8 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) if (_index != 0) { \ while (JS7_ISDEC(*_cp)) { \ _index = 10 * _index + JS7_UNDEC(*_cp); \ + if (_index < 0) \ + break; \ _cp++; \ } \ } \ @@ -983,62 +1234,84 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) } \ PR_END_MACRO -JSProperty * -js_DefineProperty(JSContext *cx, JSObject *obj, jsval id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN flags) +JSBool +js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp) { - JSRuntime *rt; + JSClass *clasp; JSScope *scope; - JSProperty *prop; - - rt = cx->runtime; - PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); + JSScopeProperty *sprop; /* Handle old bug that treated empty string as zero index. */ CHECK_FOR_FUNNY_INDEX(id); + /* Lock if object locking is required by this implementation. */ + JS_LOCK_OBJ(cx, obj); + /* Use the object's class getter and setter by default. */ + clasp = LOCKED_OBJ_GET_CLASS(obj); if (!getter) - getter = obj->map->clasp->getProperty; + getter = clasp->getProperty; if (!setter) - setter = obj->map->clasp->setProperty; + setter = clasp->setProperty; /* Find a sharable scope, or get a new one for obj. */ - scope = js_MutateScope(cx, obj, id, getter, setter, flags, &prop); + scope = js_MutateScope(cx, obj, id, getter, setter, attrs, &sprop); if (!scope) - return NULL; + goto bad; /* Add the property only if MutateScope didn't find a shared scope. */ - if (!prop) { - prop = js_NewProperty(cx, scope, id, getter, setter, flags); - if (!prop) - return NULL; - if (!obj->map->clasp->addProperty(cx, obj, prop->id, &value) || - !scope->ops->add(cx, scope, id, prop)) { - js_DestroyProperty(cx, prop); - return NULL; + if (!sprop) { + sprop = js_NewScopeProperty(cx, scope, id, getter, setter, attrs); + if (!sprop) + goto bad; + /* XXXbe called with lock held */ + if (!clasp->addProperty(cx, obj, sprop->id, &value) || + !scope->ops->add(cx, scope, id, sprop)) { + js_DestroyScopeProperty(cx, scope, sprop); + goto bad; } - PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop); + PROPERTY_CACHE_FILL(cx, &cx->runtime->propertyCache, obj, id, + (JSProperty *)sprop); } - PR_ASSERT(prop->slot < obj->map->freeslot); - obj->slots[prop->slot] = value; - return prop; + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value); + if (propp) { +#ifdef JS_THREADSAFE + js_HoldScopeProperty(cx, scope, sprop); +#endif + *propp = (JSProperty *) sprop; + } else { + JS_UNLOCK_OBJ(cx, obj); + } + return JS_TRUE; + +bad: + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; } +#if defined JS_THREADSAFE && defined DEBUG JS_FRIEND_API(JSBool) -js_LookupProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, +_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp, const char *file, uintN line) +#else +JS_FRIEND_API(JSBool) +js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) +#endif { - JSObject *pobj; PRHashNumber hash; JSScope *prevscope, *scope; JSSymbol *sym; JSClass *clasp; JSResolveOp resolve; JSNewResolveOp newresolve; - - PR_ASSERT(JS_IS_LOCKED(cx)); + uintN flags; + uint32 format; + JSObject *obj2, *proto; + JSScopeProperty *sprop; /* Handle old bug that treated empty string as zero index. */ CHECK_FOR_FUNNY_INDEX(id); @@ -1046,112 +1319,143 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, /* Search scopes starting with obj and following the prototype link. */ hash = js_HashValue(id); prevscope = NULL; - do { + for (;;) { + JS_LOCK_OBJ(cx, obj); + _SET_OBJ_INFO(obj, file, line); scope = (JSScope *)obj->map; if (scope == prevscope) - continue; + goto skip; sym = scope->ops->lookup(cx, scope, id, hash); - if (!sym && objp) { - clasp = scope->map.clasp; + if (!sym) { + clasp = LOCKED_OBJ_GET_CLASS(obj); resolve = clasp->resolve; if (resolve != JS_ResolveStub) { if (clasp->flags & JSCLASS_NEW_RESOLVE) { newresolve = (JSNewResolveOp)resolve; - pobj = NULL; - if (!newresolve(cx, obj, js_IdToValue(id), &pobj)) + flags = 0; + if (cx->fp && cx->fp->pc) { + format = js_CodeSpec[*cx->fp->pc].format; + if ((format & JOF_MODEMASK) != JOF_NAME) + flags |= JSRESOLVE_QUALIFIED; + if (format & JOF_SET) + flags |= JSRESOLVE_ASSIGNING; + } + obj2 = NULL; + JS_UNLOCK_OBJ(cx, obj); + if (!newresolve(cx, obj, js_IdToValue(id), flags, &obj2)) return JS_FALSE; - if (pobj) { - *objp = pobj; - scope = (JSScope *)pobj->map; - sym = scope->ops->lookup(cx, scope, id, hash); + JS_LOCK_OBJ(cx, obj); + _SET_OBJ_INFO(obj, file, line); + if (obj2) { + scope = (JSScope *)obj2->map; + if (MAP_IS_NATIVE(&scope->map)) + sym = scope->ops->lookup(cx, scope, id, hash); } } else { + JS_UNLOCK_OBJ(cx, obj); if (!resolve(cx, obj, js_IdToValue(id))) return JS_FALSE; + JS_LOCK_OBJ(cx, obj); + _SET_OBJ_INFO(obj, file, line); scope = (JSScope *)obj->map; - sym = scope->ops->lookup(cx, scope, id, hash); + if (MAP_IS_NATIVE(&scope->map)) + sym = scope->ops->lookup(cx, scope, id, hash); } } } - if (sym) { - *propp = sym_property(sym); + if (sym && (sprop = sym_property(sym)) != NULL) { + PR_ASSERT((JSScope *)obj->map == scope); + *objp = scope->object; /* XXXbe hide in jsscope.[ch] */ +#ifdef JS_THREADSAFE + js_HoldScopeProperty(cx, scope, sprop); +#endif + *propp = (JSProperty *) sprop; return JS_TRUE; } prevscope = scope; - } while ((obj = OBJ_GET_PROTO(obj)) != NULL); + skip: + proto = LOCKED_OBJ_GET_PROTO(obj); + JS_UNLOCK_OBJ(cx, obj); + if (!proto) + break; + if (!OBJ_IS_NATIVE(proto)) + return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); + obj = proto; + } + *objp = NULL; *propp = NULL; return JS_TRUE; } -JSBool -js_FindProperty(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp) +JS_FRIEND_API(JSBool) +js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, + JSProperty **propp) { JSRuntime *rt; - JSObject *obj, *parent, *lastobj; + JSObject *obj, *pobj, *parent, *lastobj; JSProperty *prop; rt = cx->runtime; - PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); for (obj = cx->fp->scopeChain; obj; obj = parent) { /* Try the property cache and return immediately on cache hit. */ PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop); if (PROP_FOUND(prop)) { +#ifdef JS_THREADSAFE + PR_ASSERT(OBJ_IS_NATIVE(obj)); + JS_LOCK_OBJ(cx, obj); + JS_ATOMIC_ADDREF(&((JSScopeProperty *)prop)->nrefs, 1); +#endif *objp = obj; + *pobjp = obj; *propp = prop; return JS_TRUE; } - /* - * Set parent here, after cache hit to minimize cycles in that case, - * but before js_LookupProperty, which might change obj. - */ - parent = OBJ_GET_PARENT(obj); - /* If cache miss (not cached-as-not-found), take the slow path. */ if (!prop) { - if (!js_LookupProperty(cx, obj, id, &obj, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) return JS_FALSE; if (prop) { - PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop); + PROPERTY_CACHE_FILL(cx, &rt->propertyCache, pobj, id, prop); *objp = obj; + *pobjp = pobj; *propp = prop; return JS_TRUE; } /* No such property -- cache obj[id] as not-found. */ PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, - PROP_NOT_FOUND); + PROP_NOT_FOUND(obj, id)); } + parent = OBJ_GET_PARENT(cx, obj); lastobj = obj; } *objp = lastobj; + *pobjp = NULL; *propp = NULL; return JS_TRUE; } JSBool -js_FindVariable(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp) +js_FindVariable(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, + JSProperty **propp) { - JSRuntime *rt; JSObject *obj; JSProperty *prop; - rt = cx->runtime; - PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); - /* * First look for id's property along the "with" statement and the * statically-linked scope chains. */ - if (!js_FindProperty(cx, id, objp, propp)) + if (!js_FindProperty(cx, id, objp, pobjp, propp)) return JS_FALSE; if (*propp) return JS_TRUE; /* * Use the top-level scope from the scope chain, which won't end in the - * same scope as cx->globalObject's for cross-context function calls. + * same scope as cx->globalObject for cross-context function calls. */ obj = *objp; PR_ASSERT(obj); @@ -1159,11 +1463,11 @@ js_FindVariable(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp) /* * Make a top-level variable. */ - prop = js_DefineProperty(cx, obj, id, JSVAL_VOID, NULL, NULL, - JSPROP_ENUMERATE); - if (!prop) + if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + JSPROP_ENUMERATE, &prop)) { return JS_FALSE; - PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop); + } + *pobjp = obj; *propp = prop; return JS_TRUE; } @@ -1176,12 +1480,11 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp) JSClass *clasp; JSFunction *fun; - PR_ASSERT(JS_IS_LOCKED(cx)); - fp = cx->fp; - for (obj = fp->scopeChain, withobj = NULL; ; obj = parent) { - parent = OBJ_GET_PARENT(obj); - clasp = obj->map->clasp; + withobj = NULL; + for (obj = fp->scopeChain; ; obj = parent) { + parent = OBJ_GET_PARENT(cx, obj); + clasp = OBJ_GET_CLASS(cx, obj); if (!parent || clasp != &js_WithClass) break; withobj = obj; @@ -1192,11 +1495,8 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp) if (fun && fun->script) { for (; fp && fp->fun != fun; fp = fp->down) ; - if (fp) { - obj = js_GetCallObject(cx, fp, parent); - if (withobj) - OBJ_SET_PARENT(withobj, obj); - } + if (fp) + obj = js_GetCallObject(cx, fp, parent, withobj); } #endif @@ -1204,64 +1504,80 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp) return obj; } -JSProperty * -js_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +JSBool +js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { - JSProperty *prop; JSObject *obj2; + JSScopeProperty *sprop; jsint slot; - PR_ASSERT(JS_IS_LOCKED(cx)); + if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop)) + return JS_FALSE; + if (!sprop) { + /* Handle old bug that treated empty string as zero index. */ + CHECK_FOR_FUNNY_INDEX(id); - if (!js_LookupProperty(cx, obj, id, &obj, &prop)) - return NULL; - if (!prop) { - prop = js_DefineProperty(cx, obj, id, #if JS_BUG_NULL_INDEX_PROPS - (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0) - ? JSVAL_NULL - : JSVAL_VOID, + /* Indexd properties defaulted to null in old versions. */ + *vp = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0) + ? JSVAL_NULL + : JSVAL_VOID; #else - JSVAL_VOID, + *vp = JSVAL_VOID; #endif - NULL, NULL, 0); - if (!prop) - return NULL; + + return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, js_IdToValue(id), + vp); } - obj2 = prop->object; - slot = prop->slot; - *vp = obj2->slots[slot]; - if (!prop->getter(cx, obj, prop->id, vp)) - return NULL; - obj2->slots[slot] = *vp; - return prop; + + if (!OBJ_IS_NATIVE(obj2)) { + OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop); + return OBJ_GET_PROPERTY(cx, obj2, id, vp); + } + + /* Unlock obj2 before calling getter, relock after to avoid deadlock. */ + slot = sprop->slot; + *vp = LOCKED_OBJ_GET_SLOT(obj2, slot); + JS_UNLOCK_OBJ(cx, obj2); + if (!SPROP_GET(cx, sprop, obj, obj2, vp)) { + JS_LOCK_OBJ(cx, obj2); + OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop); + return JS_FALSE; + } + JS_LOCK_OBJ(cx, obj2); + LOCKED_OBJ_SET_SLOT(obj2, slot, *vp); + PROPERTY_CACHE_FILL(cx, &cx->runtime->propertyCache, obj2, id, + (JSProperty *)sprop); + OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop); + return JS_TRUE; } -JSProperty * -js_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +JSBool +js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { JSRuntime *rt; - JSScope *scope, *protoScope; - JSProperty *prop, *protoProp; + JSScope *scope, *protoscope; + JSScopeProperty *sprop, *protosprop; PRHashNumber hash; - JSSymbol *sym, *protoSym; - JSObject *proto, *assignobj; + JSSymbol *sym, *protosym; + JSObject *proto, *tmp, *assignobj; + jsval protoid; + JSPropertyOp protogetter, protosetter; + uintN protoattrs; + JSClass *clasp; jsval pval, aval, rval; jsint slot; JSErrorReporter older; JSString *str; rt = cx->runtime; - PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); + JS_LOCK_OBJ(cx, obj); -#ifdef SCOPE_TABLE_NOTYET - /* XXX hash recompute */ - /* XXX protoProp->getter, protoProp->setter, protoProp->flags */ - scope = js_MutateScope(cx, obj, id, getter, setter, flags, &prop); -#endif scope = js_GetMutableScope(cx, obj); - if (!scope) - return NULL; + if (!scope) { + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; + } /* Handle old bug that treated empty string as zero index. */ CHECK_FOR_FUNNY_INDEX(id); @@ -1269,333 +1585,655 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) hash = js_HashValue(id); sym = scope->ops->lookup(cx, scope, id, hash); if (sym) { - prop = sym_property(sym); + sprop = sym_property(sym); #if JS_HAS_OBJ_WATCHPOINT - if (!prop) { + if (!sprop) { + uint32 slot, nslots; + jsval *slots; + /* * Deleted property place-holder, could have a watchpoint that - * holds the deleted-but-watched property. + * holds the deleted-but-watched property. If so, slots may have + * shrunk, or at least freeslot may have shrunk due to the delete + * operation destroying the property. */ - prop = js_FindWatchPoint(rt, obj, js_IdToValue(id)); + sprop = js_FindWatchPoint(rt, obj, js_IdToValue(id)); + if (sprop && (slot = sprop->slot) >= scope->map.freeslot) { + if (slot >= scope->map.nslots) { + nslots = slot + slot / 2; + slots = JS_realloc(cx, obj->slots, nslots * sizeof(jsval)); + if (!slots) { + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; + } + scope->map.nslots = nslots; + obj->slots = slots; + } + scope->map.freeslot = slot + 1; + } } #endif } else { - prop = NULL; + sprop = NULL; } - if (!prop) { + if (!sprop) { /* Find a prototype property with the same id. */ - proto = OBJ_GET_PROTO(obj); - protoProp = NULL; + proto = LOCKED_OBJ_GET_PROTO(obj); + protosprop = NULL; + + JS_UNLOCK_OBJ(cx, obj); while (proto) { - protoScope = (JSScope *)proto->map; - protoSym = protoScope->ops->lookup(cx, protoScope, id, hash); - if (protoSym) { - protoProp = sym_property(protoSym); - if (protoProp) - break; + JS_LOCK_OBJ(cx, proto); + protoscope = (JSScope *)proto->map; + if (MAP_IS_NATIVE(&protoscope->map)) { + protosym = protoscope->ops->lookup(cx, protoscope, id, hash); + if (protosym) { + protosprop = sym_property(protosym); + if (protosprop) { + protoid = protosprop->id; + protogetter = protosprop->getter; + protosetter = protosprop->setter; + protoattrs = protosprop->attrs; + JS_UNLOCK_OBJ(cx, proto); + break; + } + } } - proto = OBJ_GET_PROTO(proto); + tmp = LOCKED_OBJ_GET_PROTO(proto); + JS_UNLOCK_OBJ(cx, proto); + proto = tmp; } + JS_LOCK_OBJ(cx, obj); /* Make a new property descriptor with the right heritage. */ - if (protoProp) { - prop = js_NewProperty(cx, scope, id, - protoProp->getter, protoProp->setter, - protoProp->flags | JSPROP_ENUMERATE); - prop->id = protoProp->id; + clasp = LOCKED_OBJ_GET_CLASS(obj); + if (protosprop) { + if (protoattrs & JSPROP_READONLY) + goto _readonly; + sprop = js_NewScopeProperty(cx, scope, id, + protogetter, protosetter, + protoattrs); + sprop->id = protoid; } else { - prop = js_NewProperty(cx, scope, id, - scope->map.clasp->getProperty, - scope->map.clasp->setProperty, - JSPROP_ENUMERATE); + sprop = js_NewScopeProperty(cx, scope, id, + clasp->getProperty, clasp->setProperty, + JSPROP_ENUMERATE); + } + if (!sprop) { + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; } - if (!prop) - return NULL; - if (!obj->map->clasp->addProperty(cx, obj, prop->id, vp)) { - js_DestroyProperty(cx, prop); - return NULL; + /* XXXbe called with obj locked */ + if (!clasp->addProperty(cx, obj, sprop->id, vp)) { + js_DestroyScopeProperty(cx, scope, sprop); + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; } /* Initialize new properties to undefined. */ - obj->slots[prop->slot] = JSVAL_VOID; + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID); if (sym) { /* Null-valued symbol left behind from a delete operation. */ - sym->entry.value = js_HoldProperty(cx, prop); + sym->entry.value = js_HoldScopeProperty(cx, scope, sprop); } } if (!sym) { /* Need a new symbol as well as a new property. */ - sym = scope->ops->add(cx, scope, id, prop); - if (!sym) - return NULL; + sym = scope->ops->add(cx, scope, id, sprop); + if (!sym) { + js_DestroyScopeProperty(cx, scope, sprop); + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; + } #if JS_BUG_AUTO_INDEX_PROPS { - jsval id2 = INT_TO_JSVAL(prop->slot - JSSLOT_START); - if (!scope->ops->add(cx, scope, id2, prop)) { + jsid id2 = (jsid) INT_TO_JSVAL(sprop->slot - JSSLOT_START); + if (!scope->ops->add(cx, scope, id2, sprop)) { scope->ops->remove(cx, scope, id); - return NULL; + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; } - PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id2, prop); + PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id2, + (JSProperty *)sprop); } #endif - PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop); + PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, + (JSProperty *)sprop); } /* Get the current property value from its slot. */ - PR_ASSERT(prop->slot < obj->map->freeslot); - slot = prop->slot; - pval = obj->slots[slot]; + PR_ASSERT(sprop->slot < obj->map->freeslot); + slot = sprop->slot; + pval = LOCKED_OBJ_GET_SLOT(obj, slot); /* Evil overloaded operator assign() hack. */ if (JSVAL_IS_OBJECT(pval)) { assignobj = JSVAL_TO_OBJECT(pval); if (assignobj) { older = JS_SetErrorReporter(cx, NULL); - if (js_GetProperty(cx, assignobj, (jsval)rt->atomState.assignAtom, - &aval) && - JSVAL_IS_FUNCTION(aval) && - js_Call(cx, assignobj, aval, 1, vp, &rval)) { + JS_UNLOCK_OBJ(cx, obj); + if (OBJ_GET_PROPERTY(cx, assignobj, (jsid)rt->atomState.assignAtom, + &aval) && + JSVAL_IS_FUNCTION(cx, aval) && + js_CallFunctionValue(cx, assignobj, aval, 1, vp, &rval)) + { *vp = rval; JS_SetErrorReporter(cx, older); - prop->flags |= JSPROP_ASSIGNHACK; - return prop; + sprop->attrs |= JSPROP_ASSIGNHACK; + return JS_TRUE; } JS_SetErrorReporter(cx, older); + JS_LOCK_OBJ(cx, obj); } } /* Check for readonly *after* the assign() hack. */ - if (prop->flags & JSPROP_READONLY) { - if (!JSVERSION_IS_ECMA(cx->version)) { - str = js_ValueToSource(cx, js_IdToValue(id)); - if (str) { - JS_ReportError(cx, "%s is read-only", - JS_GetStringBytes(str)); - } - } - return NULL; + if (sprop->attrs & JSPROP_READONLY) { + +/* "readonly" can be a language extension on OSF */ +_readonly: + JS_UNLOCK_OBJ(cx, obj); + if (JSVERSION_IS_ECMA(cx->version)) + return JS_TRUE; + str = js_DecompileValueGenerator(cx, js_IdToValue(id), NULL); + if (str) + JS_ReportError(cx, "%s is read-only", JS_GetStringBytes(str)); + return JS_FALSE; } - /* Let the setter modify vp before copying from it to obj->slots[slot]. */ - if (!prop->setter(cx, obj, prop->id, vp)) - return NULL; - GC_POKE(cx, pval); - obj->slots[slot] = *vp; - - /* Setting a property makes it enumerable. */ - prop->flags |= JSPROP_ENUMERATE; - return prop; -} - -JSBool -js_DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval) -{ -#if JS_HAS_PROP_DELETE - JSProperty *prop; - - PR_ASSERT(JS_IS_LOCKED(cx)); - - if (!js_LookupProperty(cx, obj, id, NULL, &prop)) - return JS_FALSE; - if (!prop) - return JS_TRUE; - return js_DeleteProperty2(cx, obj, prop, id, rval); -#else - jsval null = JSVAL_NULL; - - *rval = JSVAL_VOID; - return (js_SetProperty(cx, obj, id, &null) != NULL); +#ifdef JS_THREADSAFE + /* Hold sprop across setter callout, and drop after, in case of delete. */ + sprop->nrefs++; #endif + + /* Avoid deadlock by unlocking obj while calling sprop's setter. */ + JS_UNLOCK_OBJ(cx, obj); + + /* Let the setter modify vp before copying from it to obj->slots[slot]. */ + if (!SPROP_SET(cx, sprop, obj, obj, vp)) { +#ifdef JS_THREADSAFE + JS_LOCK_OBJ_VOID(cx, obj, js_DropScopeProperty(cx, scope, sprop)); +#endif + return JS_FALSE; + } + + /* Relock obj until we are done with sprop. */ + JS_LOCK_OBJ(cx, obj); + +#ifdef JS_THREADSAFE + sprop = js_DropScopeProperty(cx, scope, sprop); + if (!sprop) { + /* Lost a race with someone who deleted sprop. */ + JS_UNLOCK_OBJ(cx, obj); + return JS_TRUE; + } +#endif + GC_POKE(cx, pval); + LOCKED_OBJ_SET_SLOT(obj, slot, *vp); + +#if JS_BUG_SET_ENUMERATE + /* Setting a property makes it enumerable. */ + sprop->attrs |= JSPROP_ENUMERATE; +#endif + JS_UNLOCK_OBJ(cx, obj); + return JS_TRUE; } JSBool -js_DeleteProperty2(JSContext *cx, JSObject *obj, JSProperty *prop, jsval id, - jsval *rval) +js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool noprop, ok; + JSScopeProperty *sprop; + + noprop = !prop; + if (noprop) { + if (!js_LookupProperty(cx, obj, id, &obj, &prop)) + return JS_FALSE; + if (!prop) + return JS_TRUE; + if (!OBJ_IS_NATIVE(obj)) { + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; + } + } + sprop = (JSScopeProperty *)prop; + *attrsp = sprop->attrs; + if (noprop) + OBJ_DROP_PROPERTY(cx, obj, prop); + return JS_TRUE; +} + +JSBool +js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool noprop, ok; + JSScopeProperty *sprop; + + noprop = !prop; + if (noprop) { + if (!js_LookupProperty(cx, obj, id, &obj, &prop)) + return JS_FALSE; + if (!prop) + return JS_TRUE; + if (!OBJ_IS_NATIVE(obj)) { + ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; + } + } + sprop = (JSScopeProperty *)prop; + sprop->attrs = *attrsp; + if (noprop) + OBJ_DROP_PROPERTY(cx, obj, prop); + return JS_TRUE; +} + +JSBool +js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) { #if JS_HAS_PROP_DELETE JSRuntime *rt; + JSObject *proto; + JSProperty *prop; + JSScopeProperty *sprop; JSString *str; JSScope *scope; - JSObject *proto; - PRHashNumber hash; JSSymbol *sym; rt = cx->runtime; - PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID; - if (prop->flags & JSPROP_PERMANENT) { + /* Handle old bug that treated empty string as zero index. */ + CHECK_FOR_FUNNY_INDEX(id); + + if (!js_LookupProperty(cx, obj, id, &proto, &prop)) + return JS_FALSE; + if (!prop || proto != obj) { + if (prop) + OBJ_DROP_PROPERTY(cx, proto, prop); + /* + * If no property, or the property comes from a prototype, call the + * class's delProperty hook with rval as the result parameter. + */ + return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, js_IdToValue(id), + rval); + } + + sprop = (JSScopeProperty *)prop; + if (sprop->attrs & JSPROP_PERMANENT) { + OBJ_DROP_PROPERTY(cx, obj, prop); if (JSVERSION_IS_ECMA(cx->version)) { *rval = JSVAL_FALSE; return JS_TRUE; } - str = js_ValueToSource(cx, js_IdToValue(id)); + str = js_DecompileValueGenerator(cx, js_IdToValue(id), NULL); if (str) JS_ReportError(cx, "%s is permanent", JS_GetStringBytes(str)); return JS_FALSE; } - if (!obj->map->clasp->delProperty(cx, obj, prop->id, - &prop->object->slots[prop->slot])) { + /* XXXbe called with obj locked */ + if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, sprop->id, rval)) { + OBJ_DROP_PROPERTY(cx, obj, prop); return JS_FALSE; } - /* Handle old bug that treated empty string as zero index. */ - CHECK_FOR_FUNNY_INDEX(id); - - GC_POKE(cx, prop->object->slots[prop->slot]); + GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot)); scope = (JSScope *)obj->map; - proto = scope->object; - if (proto == obj) { - /* The object has its own scope, so remove id if it was found here. */ - if (prop->object == obj) { - /* Purge cache only if prop is not about to be destroyed. */ - if (prop->nrefs != 1) { - PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, - PROP_NOT_FOUND); - } + + /* + * Purge cache only if prop is not about to be destroyed (since + * js_DestroyScopeProperty purges for us). + */ + if (sprop->nrefs != 1) { + PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, + PROP_NOT_FOUND(obj, id)); + } + #if JS_HAS_OBJ_WATCHPOINT - if (prop->setter == js_watch_set) { - /* - * Keep the symbol around with null value in case of re-set. - * The watchpoint will hold the "deleted" property until it - * is removed by obj_unwatch or a native JS_ClearWatchPoint. - * See js_SetProperty for the re-set logic. - */ - for (sym = prop->symbols; sym; sym = sym->next) { - if (sym_id(sym) == id) { - sym->entry.value = NULL; - prop = js_DropProperty(cx, prop); - PR_ASSERT(prop); - return JS_TRUE; - } - } + if (sprop->setter == js_watch_set) { + /* + * Keep the symbol around with null value in case of re-set. + * The watchpoint will hold the "deleted" property until it + * is removed by obj_unwatch or a native JS_ClearWatchPoint. + * See js_SetProperty for the re-set logic. + */ + for (sym = sprop->symbols; sym; sym = sym->next) { + if (sym_id(sym) == id) { + sym->entry.value = NULL; + sprop = js_DropScopeProperty(cx, scope, sprop); + PR_ASSERT(sprop); + goto out; } -#endif - scope->ops->remove(cx, scope, id); } - proto = OBJ_GET_PROTO(obj); - if (!proto) - return JS_TRUE; } +#endif /* JS_HAS_OBJ_WATCHPOINT */ - /* Search shared prototype scopes for an inherited property to hide. */ - hash = js_HashValue(id); - do { - scope = (JSScope *)proto->map; - sym = scope->ops->lookup(cx, scope, id, hash); - if (sym) { - /* Add a null-valued symbol to hide the prototype property. */ - scope = js_GetMutableScope(cx, obj); - if (!scope) - return JS_FALSE; - if (!scope->ops->add(cx, scope, id, NULL)) - return JS_FALSE; - PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, - PROP_NOT_FOUND); - return JS_TRUE; - } - proto = OBJ_GET_PROTO(proto); - } while (proto); + scope->ops->remove(cx, scope, id); +out: + OBJ_DROP_PROPERTY(cx, obj, prop); return JS_TRUE; -#else +#else /* !JS_HAS_PROP_DELETE */ jsval null = JSVAL_NULL; - return (js_SetProperty(cx, obj, id, &null) != NULL); -#endif + + *rval = JSVAL_VOID; + return js_SetProperty(cx, obj, id, &null); +#endif /* !JS_HAS_PROP_DELETE */ } JSBool -js_GetClassPrototype(JSContext *cx, JSClass *clasp, JSObject **protop) +js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) { - JSBool ok; - JSObject *ctor; - jsval pval; - JSProperty *prop; - - *protop = NULL; - JS_LOCK(cx); - ok = FindConstructor(cx, clasp, &pval); - if (!ok || !JSVAL_IS_FUNCTION(pval)) - goto out; - ctor = JSVAL_TO_OBJECT(pval); - if (!js_LookupProperty(cx, ctor, - (jsval)cx->runtime->atomState.classPrototypeAtom, - NULL, &prop)) { - ok = JS_FALSE; - goto out; - } - if (prop) { - pval = prop->object->slots[prop->slot]; - if (JSVAL_IS_OBJECT(pval)) - *protop = JSVAL_TO_OBJECT(pval); - } -out: - JS_UNLOCK(cx); - return ok; -} - -JSBool -js_SetClassPrototype(JSContext *cx, JSFunction *fun, JSObject *obj) -{ - JSBool ok; - - ok = JS_TRUE; - JS_LOCK(cx); - if (!js_DefineProperty(cx, fun->object, - (jsval)cx->runtime->atomState.classPrototypeAtom, - OBJECT_TO_JSVAL(obj), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - ok = JS_FALSE; - goto out; - } - if (!js_DefineProperty(cx, obj, - (jsval)cx->runtime->atomState.constructorAtom, - OBJECT_TO_JSVAL(fun->object), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - ok = JS_FALSE; - goto out; - } -out: - JS_UNLOCK(cx); - return ok; -} - -JSString * -js_ObjectToString(JSContext *cx, JSObject *obj) -{ - jsval v, *mark, *argv; + jsval v; JSString *str; - if (!obj) - return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); - v = JSVAL_VOID; - if (!obj->map->clasp->convert(cx, obj, JSTYPE_STRING, &v)) - return NULL; - if (JSVAL_IS_STRING(v)) - return JSVAL_TO_STRING(v); + v = OBJECT_TO_JSVAL(obj); + switch (hint) { + case JSTYPE_STRING: + js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v); + if (!JSVAL_IS_PRIMITIVE(v)) { + if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) + return JS_FALSE; + } + break; - /* Try the toString method if it's defined. */ - js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v); - if (JSVAL_IS_STRING(v)) - return JSVAL_TO_STRING(v); -#if JS_BUG_EAGER_TOSTRING - js_TryValueOf(cx, obj, JSTYPE_STRING, &v); - if (JSVAL_IS_STRING(v)) - return JSVAL_TO_STRING(v); + default: + if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) { + if (JS_TypeOfValue(cx, v) == hint) + goto out; + js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, + &v); + } + break; + } + if (!JSVAL_IS_PRIMITIVE(v)) { + /* Avoid recursive death through js_DecompileValueGenerator. */ + if (hint == JSTYPE_STRING) { + str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name); + if (!str) + return JS_FALSE; + } else { + str = NULL; + } + *vp = OBJECT_TO_JSVAL(obj); + str = js_DecompileValueGenerator(cx, v, str); + if (str) { + JS_ReportError(cx, "can't convert %s to %s", + JS_GetStringBytes(str), + (hint == JSTYPE_VOID) + ? "primitive type" + : js_type_str[hint]); + } + return JS_FALSE; + } +out: + *vp = v; + return JS_TRUE; +} + +extern JSIdArray * +js_NewIdArray(JSContext *cx, jsint length) +{ + JSIdArray *ida; + + ida = JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval)); + if (ida) + ida->length = length; + return ida; +} + +/* Private type used to iterate over all properties of a native JS object */ +typedef struct JSNativeIteratorState { + jsint next_index; /* index into jsid array */ + JSIdArray *ida; /* All property ids in enumeration */ +} JSNativeIteratorState; + +/* + * This function is used to enumerate the properties of native JSObjects + * and those host objects that do not define a JSNewEnumerateOp-style iterator + * function. + */ +JSBool +js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + JSClass *clasp; + JSEnumerateOp enumerate; + JSScopeProperty *sprop; + jsint i, length; + JSScope *scope; + JSIdArray *ida; + JSNativeIteratorState *state; + + clasp = OBJ_GET_CLASS(cx, obj); + enumerate = clasp->enumerate; + if (clasp->flags & JSCLASS_NEW_ENUMERATE) + return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); + + switch (enum_op) { + + case JSENUMERATE_INIT: + if (!enumerate(cx, obj)) + return JS_FALSE; + length = 0; + + /* + * The set of all property ids is pre-computed when the iterator + * is initialized so as to avoid problems with properties being + * deleted during the iteration. + */ + 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); + return JS_FALSE; + } + 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); + return JS_FALSE; + } + state->ida = ida; + state->next_index = 0; + *statep = PRIVATE_TO_JSVAL(state); + if (idp) + *idp = INT_TO_JSVAL(length); + return JS_TRUE; + + case JSENUMERATE_NEXT: + state = JSVAL_TO_PRIVATE(*statep); + ida = state->ida; + length = ida->length; + if (state->next_index != length) { + *idp = ida->vector[state->next_index++]; + return JS_TRUE; + } + + /* Fall through ... */ + + case JSENUMERATE_DESTROY: + state = JSVAL_TO_PRIVATE(*statep); + JS_free(cx, state); + *statep = JSVAL_NULL; + return JS_TRUE; + + default: + PR_ASSERT(0); + return JS_FALSE; + } +} + +JSBool +js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + JSObject *pobj; + JSProperty *prop; + JSScopeProperty *sprop; + JSClass *clasp; + JSBool ok; + + if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (!prop) { + *vp = JSVAL_VOID; + *attrsp = 0; + return JS_TRUE; + } + if (!OBJ_IS_NATIVE(pobj)) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp); + } + sprop = (JSScopeProperty *)prop; + *vp = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); + *attrsp = sprop->attrs; + clasp = LOCKED_OBJ_GET_CLASS(obj); + if (clasp->checkAccess) { + JS_UNLOCK_OBJ(cx, pobj); + ok = clasp->checkAccess(cx, obj, sprop->id, mode, vp); + JS_LOCK_OBJ(cx, pobj); + } else { + ok = JS_TRUE; + } + OBJ_DROP_PROPERTY(cx, pobj, prop); + return ok; +} + +JSBool +js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); + if (!clasp->call) { + js_ReportIsNotFunction(cx, &argv[-2], JS_FALSE); + return JS_FALSE; + } + return clasp->call(cx, obj, argc, argv, rval); +} + +JSBool +js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); + if (!clasp->construct) { + js_ReportIsNotFunction(cx, &argv[-2], JS_TRUE); + return JS_FALSE; + } + return clasp->construct(cx, obj, argc, argv, rval); +} + +JSBool +js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->hasInstance) + return clasp->hasInstance(cx, obj, v, bp); + *bp = JS_FALSE; + return JS_TRUE; +} + +JSBool +js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSObject *obj2; + + *bp = JS_FALSE; + if (JSVAL_IS_PRIMITIVE(v)) + return JS_TRUE; + obj2 = JSVAL_TO_OBJECT(v); + do { + if (obj2 == obj) { + *bp = JS_TRUE; + break; + } + } while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL); + return JS_TRUE; +} + +#ifdef JS_THREADSAFE +void +js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop) +{ + js_DropScopeProperty(cx, (JSScope *)obj->map, (JSScopeProperty *)prop); + JS_UNLOCK_OBJ(cx, obj); +} #endif - mark = PR_ARENA_MARK(&cx->stackPool); - PR_ARENA_ALLOCATE(argv, &cx->stackPool, OBJ_TOSTRING_NARGS * sizeof(jsval)); - if (!argv || !js_obj_toString(cx, obj, OBJ_TOSTRING_NARGS, argv, &v)) - str = NULL; - else - str = JSVAL_TO_STRING(v); - PR_ARENA_RELEASE(&cx->stackPool, mark); - return str; + +JSBool +js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop) +{ + jsval v; + JSObject *ctor; + + if (!FindConstructor(cx, name, &v)) + return JS_FALSE; + if (JSVAL_IS_FUNCTION(cx, v)) { + ctor = JSVAL_TO_OBJECT(v); + if (!OBJ_GET_PROPERTY(cx, ctor, + (jsid)cx->runtime->atomState.classPrototypeAtom, + &v)) { + return JS_FALSE; + } + } + *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL; + return JS_TRUE; +} + +JSBool +js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, + uintN attrs) +{ + /* + * Use the given attributes for the prototype property of the constructor, + * as user-defined constructors have a DontEnum prototype (it can be reset + * or even deleted), while native "system" constructors require DontEnum | + * ReadOnly | DontDelete. + */ + if (!OBJ_DEFINE_PROPERTY(cx, ctor, + (jsid)cx->runtime->atomState.classPrototypeAtom, + OBJECT_TO_JSVAL(proto), NULL, NULL, + attrs, NULL)) { + return JS_FALSE; + } + + /* + * ECMA says that Object.prototype.constructor, or f.prototype.constructor + * for a user-defined function f, is DontEnum. + */ + return OBJ_DEFINE_PROPERTY(cx, proto, + (jsid)cx->runtime->atomState.constructorAtom, + OBJECT_TO_JSVAL(ctor), NULL, NULL, + 0, NULL); } JSBool @@ -1607,7 +2245,7 @@ js_ValueToObject(JSContext *cx, jsval v, JSObject **objp) obj = NULL; } else if (JSVAL_IS_OBJECT(v)) { obj = JSVAL_TO_OBJECT(v); - if (!obj->map->clasp->convert(cx, obj, JSTYPE_OBJECT, &v)) + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v)) return JS_FALSE; if (JSVAL_IS_OBJECT(v)) obj = JSVAL_TO_OBJECT(v); @@ -1633,15 +2271,15 @@ JSObject * js_ValueToNonNullObject(JSContext *cx, jsval v) { JSObject *obj; - JSString *name; + JSString *str; if (!js_ValueToObject(cx, v, &obj)) return NULL; if (!obj) { - name = js_ValueToSource(cx, v); - if (name) { + str = js_DecompileValueGenerator(cx, v, NULL); + if (str) { JS_ReportError(cx, "%s has no properties", - JS_GetStringBytes(name)); + JS_GetStringBytes(str)); } } return obj; @@ -1665,15 +2303,84 @@ js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, uintN argc, jsval *argv, jsval *rval) { JSErrorReporter older; - JSProperty *prop; jsval fval; older = JS_SetErrorReporter(cx, NULL); - JS_LOCK_VOID(cx, prop = js_GetProperty(cx, obj, (jsval)atom, &fval)); - if (prop && + if (OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval) && JSVAL_IS_OBJECT(fval) && fval != JSVAL_NULL) { - (void) js_Call(cx, obj, fval, argc, argv, rval); + (void) js_CallFunctionValue(cx, obj, fval, argc, argv, rval); } JS_SetErrorReporter(cx, older); } + +#if JS_HAS_XDR + +#include "jsxdrapi.h" + +JSBool +js_XDRObject(JSXDRState *xdr, JSObject **objp) +{ + JSContext *cx; + JSClass *clasp; + char *className; + uint32 classId, classDef; + JSBool ok; + JSObject *proto; + + cx = xdr->cx; + if (xdr->mode == JSXDR_ENCODE) { + clasp = OBJ_GET_CLASS(cx, *objp); + className = clasp->name; + classId = JS_FindClassIdByName(xdr, className); + classDef = !classId; + if (classDef && !JS_RegisterClass(xdr, clasp, &classId)) + return JS_FALSE; + } else { + classDef = 0; + className = NULL; + } + + /* XDR a flag word followed (if true) by the class name. */ + if (!JS_XDRUint32(xdr, &classDef)) + return JS_FALSE; + if (classDef && !JS_XDRCString(xdr, &className)) + return JS_FALSE; + + /* From here on, return through out: to free className if it was set. */ + ok = JS_XDRUint32(xdr, &classId); + if (!ok) + goto out; + + if (xdr->mode != JSXDR_ENCODE) { + if (classDef) { + ok = js_GetClassPrototype(cx, className, &proto); + if (!ok) + goto out; + clasp = OBJ_GET_CLASS(cx, proto); + ok = JS_RegisterClass(xdr, clasp, &classId); + if (!ok) + goto out; + } else { + clasp = JS_FindClassById(xdr, classId); + if (!clasp) { + JS_ReportError(cx, "can't find class id %ld", (long)classId); + ok = JS_FALSE; + goto out; + } + } + } + + if (!clasp->xdrObject) { + JS_ReportError(cx, "can't XDR class %s", clasp->name); + ok = JS_FALSE; + } else { + ok = clasp->xdrObject(xdr, objp); + } +out: + if (xdr->mode != JSXDR_ENCODE && className) + JS_free(cx, className); + return ok; +} + +#endif /* JS_HAS_XDR */ diff --git a/mozilla/js/src/jsobj.h b/mozilla/js/src/jsobj.h index 3d028013a9b..17825396802 100644 --- a/mozilla/js/src/jsobj.h +++ b/mozilla/js/src/jsobj.h @@ -40,12 +40,48 @@ PR_BEGIN_EXTERN_C struct JSObjectMap { jsrefcount nrefs; /* count of all referencing objects */ + JSObjectOps *ops; /* high level object operation vtable */ uint32 nslots; /* length of obj->slots vector */ uint32 freeslot; /* index of next free obj->slots element */ - JSClass *clasp; /* class name and operations */ - JSProperty *props; /* property list in definition order */ }; +/* Shorthand macros for frequently-made calls. */ +#if defined JS_THREADSAFE && defined DEBUG +#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp) \ + (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__) +#else +#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp) \ + (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp) +#endif +#define OBJ_DEFINE_PROPERTY(cx,obj,id,value,getter,setter,attrs,propp) \ + (obj)->map->ops->defineProperty(cx,obj,id,value,getter,setter,attrs,propp) +#define OBJ_GET_PROPERTY(cx,obj,id,vp) \ + (obj)->map->ops->getProperty(cx,obj,id,vp) +#define OBJ_SET_PROPERTY(cx,obj,id,vp) \ + (obj)->map->ops->setProperty(cx,obj,id,vp) +#define OBJ_GET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ + (obj)->map->ops->getAttributes(cx,obj,id,prop,attrsp) +#define OBJ_SET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ + (obj)->map->ops->setAttributes(cx,obj,id,prop,attrsp) +#define OBJ_DELETE_PROPERTY(cx,obj,id,rval) \ + (obj)->map->ops->deleteProperty(cx,obj,id,rval) +#define OBJ_DEFAULT_VALUE(cx,obj,hint,vp) \ + (obj)->map->ops->defaultValue(cx,obj,hint,vp) +#define OBJ_ENUMERATE(cx,obj,enum_op,statep,idp) \ + (obj)->map->ops->enumerate(cx,obj,enum_op,statep,idp) +#define OBJ_CHECK_ACCESS(cx,obj,id,mode,vp,attrsp) \ + (obj)->map->ops->checkAccess(cx,obj,id,mode,vp,attrsp) + +/* These two are time-optimized to avoid stub calls. */ +#define OBJ_THIS_OBJECT(cx,obj) \ + ((obj)->map->ops->thisObject \ + ? (obj)->map->ops->thisObject(cx,obj) \ + : (obj)) +#define OBJ_DROP_PROPERTY(cx,obj,prop) \ + ((obj)->map->ops->dropProperty \ + ? (obj)->map->ops->dropProperty(cx,obj,prop) \ + : (void)0) + struct JSObject { JSObjectMap *map; jsval *slots; @@ -53,30 +89,71 @@ struct JSObject { #define JSSLOT_PROTO 0 #define JSSLOT_PARENT 1 -#define JSSLOT_PRIVATE 2 -#define JSSLOT_START 2 +#define JSSLOT_CLASS 2 +#define JSSLOT_PRIVATE 3 +#define JSSLOT_START 3 + +#define JSSLOT_FREE(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE) \ + ? JSSLOT_PRIVATE + 1 \ + : JSSLOT_START) #define JS_INITIAL_NSLOTS 5 -/* - * Fast get and set macros for well-known slots. These must be called within - * JS_LOCK(cx) and JS_UNLOCK(cx). - */ #ifdef DEBUG -#define MAP_CHECK_SLOT(m,s) PR_ASSERT(s < PR_MAX((m)->nslots, (m)->freeslot)) -#define OBJ_CHECK_SLOT(o,s) MAP_CHECK_SLOT((o)->map,s) +#define MAP_CHECK_SLOT(map,slot) \ + PR_ASSERT((uint32)slot < PR_MAX((map)->nslots, (map)->freeslot)) +#define OBJ_CHECK_SLOT(obj,slot) \ + MAP_CHECK_SLOT((obj)->map, slot) #else -#define OBJ_CHECK_SLOT(o,s) ((void)0) +#define OBJ_CHECK_SLOT(obj,slot) ((void)0) #endif -#define OBJ_GET_SLOT(o,s) (OBJ_CHECK_SLOT(o,s), (o)->slots[s]) -#define OBJ_SET_SLOT(o,s,v) (OBJ_CHECK_SLOT(o,s), (o)->slots[s] = (v)) -#define OBJ_GET_PROTO(o) JSVAL_TO_OBJECT(OBJ_GET_SLOT(o,JSSLOT_PROTO)) -#define OBJ_SET_PROTO(o,p) OBJ_SET_SLOT(o,JSSLOT_PROTO,OBJECT_TO_JSVAL(p)) -#define OBJ_GET_PARENT(o) JSVAL_TO_OBJECT(OBJ_GET_SLOT(o,JSSLOT_PARENT)) -#define OBJ_SET_PARENT(o,p) OBJ_SET_SLOT(o,JSSLOT_PARENT,OBJECT_TO_JSVAL(p)) -extern JSClass js_ObjectClass; -extern JSClass js_WithClass; +/* Fast macros for accessing obj->slots while obj is locked (if thread-safe). */ +#define LOCKED_OBJ_GET_SLOT(obj,slot) \ + (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot]) +#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \ + (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot] = (value)) +#define LOCKED_OBJ_GET_PROTO(obj) \ + JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)) +#define LOCKED_OBJ_GET_CLASS(obj) \ + ((JSClass *)JSVAL_TO_PRIVATE(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_CLASS))) + +#ifdef JS_THREADSAFE + +/* Thread-safe functions and wrapper macros for accessing obj->slots. */ +#define OBJ_GET_SLOT(cx,obj,slot) \ + (OBJ_CHECK_SLOT(obj, slot), js_GetSlotWhileLocked(cx, obj, slot)) +#define OBJ_SET_SLOT(cx,obj,slot,value) \ + (OBJ_CHECK_SLOT(obj, slot), js_SetSlotWhileLocked(cx, obj, slot, value)) + +#else /* !JS_THREADSAFE */ + +#define OBJ_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) +#define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_SET_SLOT(obj,slot,value) + +#endif /* !JS_THREADSAFE */ + +/* Thread-safe proto, parent, and class access macros. */ +#define OBJ_GET_PROTO(cx,obj) \ + JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO)) +#define OBJ_SET_PROTO(cx,obj,proto) \ + OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)) + +#define OBJ_GET_PARENT(cx,obj) \ + JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT)) +#define OBJ_SET_PARENT(cx,obj,parent) \ + OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)) + +#define OBJ_GET_CLASS(cx,obj) \ + ((JSClass *)JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, obj, JSSLOT_CLASS))) + +/* Test whether a map or object is native. */ +#define MAP_IS_NATIVE(map) ((map)->ops == &js_ObjectOps) +#define OBJ_IS_NATIVE(obj) MAP_IS_NATIVE((obj)->map) + +extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps; +extern JSClass js_ObjectClass; +extern JSClass js_WithClass; struct JSSharpObjectMap { jsrefcount depth; @@ -89,10 +166,15 @@ struct JSSharpObjectMap { #define MAKE_SHARP(he) ((he)->value = (void*)((jsatomid)(he)->value|SHARP_BIT)) extern PRHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, jschar **sp); +js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, + jschar **sp); extern void -js_LeaveSharpObject(JSContext *cx); +js_LeaveSharpObject(JSContext *cx, JSIdArray **idap); + +extern JSBool +js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); extern JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, @@ -101,6 +183,23 @@ js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, extern JSObject * js_InitObjectClass(JSContext *cx, JSObject *obj); +extern void +js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, + JSClass *clasp); + +extern JSObjectMap * +js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, + JSClass *clasp, JSObject *obj); + +extern void +js_DestroyObjectMap(JSContext *cx, JSObjectMap *map); + +extern JSObjectMap * +js_HoldObjectMap(JSContext *cx, JSObjectMap *map); + +extern JSObjectMap * +js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj); + extern JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); @@ -111,60 +210,102 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, extern void js_FinalizeObject(JSContext *cx, JSObject *obj); -extern jsval -js_GetSlot(JSContext *cx, JSObject *obj, uint32 slot); - -extern JSBool -js_SetSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval value); - extern JSBool js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp); extern void js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); -extern JSProperty * -js_DefineProperty(JSContext *cx, JSObject *obj, jsval id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN flags); - /* - * Pass &obj for objp if you want to resolve lazily bound properties. In that - * case, *objp may differ from obj on return; caveat callers. + * On error, return false. On success, if propp is non-null, return true with + * obj locked and with a held property in *propp; if propp is null, return true + * but release obj's lock first. Therefore all callers who pass non-null propp + * result parameters must later call OBJ_DROP_PROPERTY(cx, obj, *propp) both to + * drop the held property, and to release the lock on obj. */ -extern JS_FRIEND_API(JSBool) -js_LookupProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, +extern JSBool +js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, JSProperty **propp); -extern JSBool -js_FindProperty(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp); +/* + * Unlike js_DefineProperty, propp must be non-null. On success, and if id was + * found, return true with *objp non-null and locked, and with a held property + * stored in *propp. If successful but id was not found, return true with both + * *objp and *propp null. Therefore all callers who receive a non-null *propp + * must later call OBJ_DROP_PROPERTY(cx, *objp, *propp). + */ +#if defined JS_THREADSAFE && defined DEBUG +extern JS_FRIEND_API(JSBool) +_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp, const char *file, uintN line); + +#define js_LookupProperty(cx,obj,id,objp,propp) \ + _js_LookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__) +#else +extern JS_FRIEND_API(JSBool) +js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp); +#endif + +extern JS_FRIEND_API(JSBool) +js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, + JSProperty **propp); extern JSBool -js_FindVariable(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp); +js_FindVariable(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, + JSProperty **propp); extern JSObject * js_FindVariableScope(JSContext *cx, JSFunction **funp); -extern JSProperty * -js_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSProperty * -js_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); +extern JSBool +js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); extern JSBool -js_DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval); +js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); extern JSBool -js_DeleteProperty2(JSContext *cx, JSObject *obj, JSProperty *prop, jsval id, - jsval *rval); +js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp); extern JSBool -js_GetClassPrototype(JSContext *cx, JSClass *clasp, JSObject **protop); +js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp); extern JSBool -js_SetClassPrototype(JSContext *cx, JSFunction *fun, JSObject *obj); +js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval); -extern JSString * -js_ObjectToString(JSContext *cx, JSObject *obj); +extern JSBool +js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp); + +extern JSBool +js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp); + +extern JSBool +js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp); + +extern JSBool +js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +extern JSBool +js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JSBool +js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +extern JSBool +js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +extern JSBool +js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop); + +extern JSBool +js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, + uintN attrs); extern JSBool js_ValueToObject(JSContext *cx, jsval v, JSObject **objp); @@ -179,6 +320,12 @@ extern void js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, uintN argc, jsval *argv, jsval *rval); +extern JSBool +js_XDRObject(JSXDRState *xdr, JSObject **objp); + +extern JSIdArray * +js_NewIdArray(JSContext *cx, jsint length); + PR_END_EXTERN_C #endif /* jsobj_h___ */ diff --git a/mozilla/js/src/jsopcode.c b/mozilla/js/src/jsopcode.c index 6f559aa1afd..bfd5e4c6282 100644 --- a/mozilla/js/src/jsopcode.c +++ b/mozilla/js/src/jsopcode.c @@ -48,16 +48,18 @@ #include "jsscript.h" #include "jsstr.h" -char js_new_str[] = "new"; -char js_delete_str[] = "delete"; -char js_typeof_str[] = "typeof"; -char js_void_str[] = "void"; -char js_null_str[] = "null"; -char js_this_str[] = "this"; -char js_false_str[] = "false"; -char js_true_str[] = "true"; +char js_in_str[] = "in"; +char js_instanceof_str[] = "instanceof"; +char js_new_str[] = "new"; +char js_delete_str[] = "delete"; +char js_typeof_str[] = "typeof"; +char js_void_str[] = "void"; +char js_null_str[] = "null"; +char js_this_str[] = "this"; +char js_false_str[] = "false"; +char js_true_str[] = "true"; -char *js_incop_str[] = {"++", "--"}; +char *js_incop_str[] = {"++", "--"}; /* Pollute the namespace locally for MSVC Win16, but not for WatCom. */ #ifdef __WINDOWS_386__ @@ -83,7 +85,7 @@ uintN js_NumCodeSpecs = sizeof js_CodeSpec / sizeof js_CodeSpec[0]; #ifdef DEBUG -void +JS_FRIEND_API(void) js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp) { jsbytecode *pc, *end; @@ -99,7 +101,7 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp) } } -uintN +JS_FRIEND_API(uintN) js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, JSBool lines, FILE *fp) { @@ -112,7 +114,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, op = *pc; if (op >= JSOP_LIMIT) { - JS_ReportError(cx, "bytecode %d too large", op); + JS_ReportError(cx, "bytecode %d too large (limit %d)", op, JSOP_LIMIT); return 0; } cs = &js_CodeSpec[op]; @@ -161,17 +163,17 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, pc2 = pc; off = GET_JUMP_OFFSET(pc2); end = pc + off; - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; fprintf(fp, " defaultOffset %d low %d high %d", off, low, high); if (pc2 + 1 < end) { for (i = low; i <= high; i++) { off = GET_JUMP_OFFSET(pc2); fprintf(fp, "\n\t%d: %d", i, off); - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; } } len = 1 + pc2 - pc; @@ -185,15 +187,15 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, jsval key; off = GET_JUMP_OFFSET(pc2); - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; npairs = (uintN) GET_ATOM_INDEX(pc2); - pc2 += 2; + pc2 += ATOM_INDEX_LEN; fprintf(fp, " defaultOffset %d npairs %u", off, npairs); while (npairs) { atom = GET_ATOM(cx, script, pc2); - pc2 += 2; + pc2 += ATOM_INDEX_LEN; off = GET_JUMP_OFFSET(pc2); - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; key = ATOM_KEY(atom); str = js_ValueToString(cx, key); @@ -319,7 +321,7 @@ jschar js_EscapeMap[] = { }; static char * -EscapeString(Sprinter *sp, JSString *str, jschar quote) +QuoteString(Sprinter *sp, JSString *str, jschar quote) { ptrdiff_t off, len, nb; const jschar *s, *t, *u; @@ -331,6 +333,8 @@ EscapeString(Sprinter *sp, JSString *str, jschar quote) s = str->chars; t = s; c = *t; + if (Sprint(sp, "%c", (char)quote) < 0) + return NULL; do { while (JS_ISPRINT(c) && c != quote && !(c >> 8)) c = *++t; @@ -359,11 +363,13 @@ EscapeString(Sprinter *sp, JSString *str, jschar quote) s = ++t; c = *t; } while (c != 0); + if (Sprint(sp, "%c", (char)quote) < 0) + return NULL; return OFF2STR(sp, off); } JSString * -js_EscapeString(JSContext *cx, JSString *str, jschar quote) +js_QuoteString(JSContext *cx, JSString *str, jschar quote) { void *mark; Sprinter sprinter; @@ -372,7 +378,7 @@ js_EscapeString(JSContext *cx, JSString *str, jschar quote) mark = PR_ARENA_MARK(&cx->tempPool); INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - bytes = EscapeString(&sprinter, str, quote); + bytes = QuoteString(&sprinter, str, quote); escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; PR_ARENA_RELEASE(&cx->tempPool, mark); return escstr; @@ -385,12 +391,16 @@ struct JSPrinter { PRArenaPool pool; /* string allocation pool */ uintN indent; /* indentation in spaces */ JSScript *script; /* script being printed */ + JSScope *scope; /* script function scope */ }; JSPrinter * js_NewPrinter(JSContext *cx, const char *name, uintN indent) { JSPrinter *jp; + JSStackFrame *fp; + JSObject *obj; + JSObjectMap *map; jp = JS_malloc(cx, sizeof(JSPrinter)); if (!jp) @@ -399,6 +409,20 @@ js_NewPrinter(JSContext *cx, const char *name, uintN indent) PR_InitArenaPool(&jp->pool, name, 256, 1); jp->indent = indent; jp->script = NULL; + jp->scope = NULL; + fp = cx->fp; + if (fp && fp->scopeChain) { + obj = fp->scopeChain; + map = obj->map; + if (map->ops == &js_ObjectOps) { + if (OBJ_GET_CLASS(cx, obj) == &js_CallClass) { + obj = fp->fun->object; + if (obj) + map = obj->map; + } + jp->scope = (JSScope *)map; + } + } return jp; } @@ -477,16 +501,17 @@ typedef struct SprintStack { #define PAREN_SLOP (2 + 1) /* - * These pseudo-ops help js_ValueToSource decompile JSOP_SETNAME, JSOP_SETPROP, - * and JSOP_SETELEM, respectively. See the first assertion in PushOff. + * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME, + * JSOP_SETPROP, and JSOP_SETELEM, respectively. See the first assertion in + * PushOff. */ -#define JSOP_GETPROP2 (JSOP_GETPROP + 128) -#define JSOP_GETELEM2 (JSOP_GETELEM + 128) +#define JSOP_GETPROP2 254 +#define JSOP_GETELEM2 255 static JSBool PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) { - PR_ASSERT(JSOP_LIMIT <= 128); + PR_ASSERT(JSOP_LIMIT <= 254); if (!SprintAlloc(&ss->sprinter, PAREN_SLOP)) return JS_FALSE; PR_ASSERT(ss->top < ss->printer->script->depth); @@ -574,11 +599,13 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, return JS_FALSE; jp->indent += 2; if (JSVAL_IS_STRING(key)) { - rval = EscapeString(&ss->sprinter, str, '"'); - js_printf(jp, "\tcase \"%s\":\n", rval); + rval = QuoteString(&ss->sprinter, str, '"'); + if (!rval) + return JS_FALSE; } else { - js_printf(jp, "\tcase %s:\n", JS_GetStringBytes(str)); + rval = JS_GetStringBytes(str); } + js_printf(jp, "\tcase %s:\n", rval); jp->indent += 2; if (off <= defaultOffset && defaultOffset < off2) { diff = defaultOffset - off; @@ -607,11 +634,17 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, #endif static JSAtom * -GetSlotAtom(JSSymbol *sym, uintN slot) +GetSlotAtom(JSScope *scope, JSPropertyOp getter, uintN slot) { - for (; sym; sym = sym->next) { - if ((uintN)JSVAL_TO_INT(sym_property(sym)->id) == slot) - return sym_atom(sym); + JSScopeProperty *sprop; + + if (!scope) + return NULL; + for (sprop = scope->props; sprop; sprop = sprop->next) { + if (sprop->getter != getter) + continue; + if ((uintN)JSVAL_TO_INT(sprop->id) == slot) + return sym_atom(sprop->symbols); } return NULL; } @@ -622,7 +655,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) JSContext *cx; JSPrinter *jp; jsbytecode *endpc, *done; - ptrdiff_t len, todo, cond, next, tail; + ptrdiff_t len, todo, oplen, cond, next, tail; JSOp op, lastop, saveop; JSCodeSpec *cs, *topcs; jssrcnote *sn; @@ -630,6 +663,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) jsint i, argc; char **argv; JSAtom *atom; + JSString *str; JSBool ok; jsval key; @@ -647,16 +681,27 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) op = JSOP_NOP; while (pc < endpc) { lastop = op; - op = *pc; - cs = &js_CodeSpec[op >= 128 ? op - 128 : op]; - len = cs->length; + op = saveop = *pc; + if (op >= JSOP_LIMIT) { + switch (op) { + case JSOP_GETPROP2: + saveop = JSOP_GETPROP; + break; + case JSOP_GETELEM2: + saveop = JSOP_GETELEM; + break; + default:; + } + } + cs = &js_CodeSpec[saveop]; + len = oplen = cs->length; if (cs->token) { switch (cs->nuses) { case 2: rval = POP_STR(); lval = POP_STR(); - if ((sn = js_GetSrcNote(jp->script, pc)) && + if ((sn = js_GetSrcNote(jp->script, pc)) != NULL && SN_TYPE(sn) == SRC_ASSIGNOP) { /* Print only the right operand of the assignment-op. */ todo = SprintPut(&ss->sprinter, rval, strlen(rval)); @@ -684,8 +729,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_NOP: /* * Check for extra user parenthesization, a do-while loop, - * a for-loop with an empty initializer part, or a labeled - * statement. + * a for-loop with an empty initializer part, a labeled + * statement, a function definition, or try/finally. */ sn = js_GetSrcNote(jp->script, pc); todo = -2; @@ -713,7 +758,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case SRC_FOR: rval = ""; - forloop: + do_forloop: /* Skip the JSOP_NOP or JSOP_POP bytecode. */ pc++; @@ -744,12 +789,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* Do the loop body. */ js_puts(jp, ") {\n"); jp->indent += 4; - DECOMPILE_CODE(pc + cond + 3, next - cond - 3); + oplen = js_CodeSpec[pc[cond]].length; + DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen); jp->indent -= 4; js_printf(jp, "\t}\n"); /* Set len so pc skips over the entire loop. */ - len = tail + 3; + len = tail + js_CodeSpec[pc[tail]].length; break; case SRC_LABEL: @@ -772,12 +818,69 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) js_printf(jp, "\t}\n"); break; + case SRC_TRY: + js_printf(jp, "\ttry {\n"); + jp->indent += 4; + break; + + case SRC_CATCH: + jp->indent -= 4; + 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); + } + js_printf(jp, ") {\n"); /* balance} */ + jp->indent += 4; + break; + + case SRC_FUNCDEF: { + JSPrinter *jp2; + JSObject *obj; + JSFunction *fun; + + atom = js_GetAtom(cx, &jp->script->atomMap, + (jsatomid) js_GetSrcNoteOffset(sn, 0)); + PR_ASSERT(ATOM_IS_OBJECT(atom)); + obj = ATOM_TO_OBJECT(atom); + fun = JS_GetPrivate(cx, obj); + jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun), + jp->indent); + if (!jp2) + return JS_FALSE; + jp2->scope = jp->scope; + if (js_DecompileFunction(jp2, fun, JS_TRUE)) { + str = js_GetPrinterOutput(jp2); + if (str) + js_printf(jp, "%s\n", JS_GetStringBytes(str)); + } + js_DestroyPrinter(jp2); + break; + } default:; } break; case JSOP_PUSH: case JSOP_PUSHOBJ: + case JSOP_BINDNAME: todo = Sprint(&ss->sprinter, ""); break; @@ -788,7 +891,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case SRC_FOR: rval = POP_STR(); todo = -2; - goto forloop; + goto do_forloop; case SRC_COMMA: /* Pop and save to avoid blowing stack depth budget. */ @@ -799,9 +902,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* The offset tells distance to next comma, or to end. */ done = pc + len; pc += js_GetSrcNoteOffset(sn, 0); - LOCAL_ASSERT(pc == endpc || - *pc == JSOP_POP || *pc == JSOP_POPV || - *pc == JSOP_NOP); + LOCAL_ASSERT(pc == endpc || *pc == JSOP_POP); + len = 0; if (!Decompile(ss, done, pc - done)) { JS_free(cx, (char *)lval); @@ -812,7 +914,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = POP_STR(); todo = Sprint(&ss->sprinter, "%s, %s", lval, rval); JS_free(cx, (char *)lval); - len = 0; break; case SRC_HIDDEN: @@ -829,6 +930,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } break; + case JSOP_POP2: + (void) PopOff(ss, op); + (void) PopOff(ss, op); + todo = -2; + break; + case JSOP_ENTERWITH: rval = POP_STR(); js_printf(jp, "\twith (%s) {\n", rval); @@ -854,6 +961,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = -2; break; + case JSOP_THROW: + rval = POP_STR(); + js_printf(jp, "\t%s %s;\n", cs->name, rval); + todo = -2; + break; + case JSOP_GOTO: sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { @@ -870,6 +983,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) (jsatomid) js_GetSrcNoteOffset(sn, 0)); js_printf(jp, "\tbreak %s;\n", ATOM_BYTES(atom)); break; + case SRC_HIDDEN: + break; default: js_printf(jp, "\tbreak;\n"); break; @@ -888,15 +1003,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) js_printf(jp, "\tif (%s) {\n", rval); jp->indent += 4; if (SN_TYPE(sn) == SRC_IF) { - DECOMPILE_CODE(pc + 3, len - 3); + DECOMPILE_CODE(pc + oplen, len - oplen); } else { - DECOMPILE_CODE(pc + 3, len - 6); + DECOMPILE_CODE(pc + oplen, + len - (oplen + js_CodeSpec[JSOP_GOTO].length)); jp->indent -= 4; - pc += len - 3; + pc += len - oplen; + LOCAL_ASSERT(*pc == JSOP_GOTO); + oplen = js_CodeSpec[JSOP_GOTO].length; len = GET_JUMP_OFFSET(pc); js_printf(jp, "\t} else {\n"); jp->indent += 4; - DECOMPILE_CODE(pc + 3, len - 3); + DECOMPILE_CODE(pc + oplen, len - oplen); } jp->indent -= 4; js_printf(jp, "\t}\n"); @@ -907,7 +1025,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = POP_STR(); js_printf(jp, "\twhile (%s) {\n", rval); jp->indent += 4; - DECOMPILE_CODE(pc + 3, len - 6); + DECOMPILE_CODE(pc + oplen, + len - (oplen + js_CodeSpec[JSOP_GOTO].length)); jp->indent -= 4; js_printf(jp, "\t}\n"); todo = -2; @@ -917,16 +1036,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) xval = JS_strdup(cx, POP_STR()); if (!xval) return JS_FALSE; - DECOMPILE_CODE(pc + 3, len - 6); + DECOMPILE_CODE(pc + oplen, + len - (oplen + js_CodeSpec[JSOP_GOTO].length)); lval = JS_strdup(cx, POP_STR()); if (!lval) { JS_free(cx, (void *)xval); return JS_FALSE; } - pc += len - 3; + pc += len - oplen; LOCAL_ASSERT(*pc == JSOP_GOTO); + oplen = js_CodeSpec[JSOP_GOTO].length; len = GET_JUMP_OFFSET(pc); - DECOMPILE_CODE(pc + 3, len - 3); + DECOMPILE_CODE(pc + oplen, len - oplen); rval = POP_STR(); todo = Sprint(&ss->sprinter, "%s ? %s : %s", xval, lval, rval); @@ -944,18 +1065,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (!lval) return JS_FALSE; ifeq = pc + len; - LOCAL_ASSERT(pc[3] == JSOP_TRUE); - pc += 4; + LOCAL_ASSERT(pc[oplen] == JSOP_TRUE); + pc += oplen + js_CodeSpec[JSOP_TRUE].length; LOCAL_ASSERT(*pc == JSOP_GOTO); + oplen = js_CodeSpec[JSOP_GOTO].length; done = pc + GET_JUMP_OFFSET(pc); - pc += 3; + pc += oplen; DECOMPILE_CODE(pc, done - ifeq); rval = POP_STR(); todo = Sprint(&ss->sprinter, "%s || %s", lval, rval); JS_free(cx, (void *)lval); len = done - pc; } -#endif +#endif /* JS_BUG_SHORT_CIRCUIT */ break; } break; @@ -982,19 +1104,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (!lval) return JS_FALSE; ifne = pc + GET_JUMP_OFFSET(pc); - len++; - pc += len; - LOCAL_ASSERT(pc[-1] == JSOP_FALSE); + LOCAL_ASSERT(pc[oplen] == JSOP_FALSE); + pc += len + js_CodeSpec[JSOP_FALSE].length; LOCAL_ASSERT(*pc == JSOP_GOTO); + oplen = js_CodeSpec[JSOP_GOTO].length; done = pc + GET_JUMP_OFFSET(pc); - pc += 3; + pc += oplen; DECOMPILE_CODE(pc, done - ifne); rval = POP_STR(); todo = Sprint(&ss->sprinter, "%s && %s", lval, rval); JS_free(cx, (void *)lval); len = done - pc; } -#endif +#endif /* JS_BUG_SHORT_CIRCUIT */ break; #if !JS_BUG_SHORT_CIRCUIT @@ -1028,40 +1150,53 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) #endif case JSOP_FORNAME: - atom = GET_ATOM(cx, jp->script, pc); rval = POP_STR(); - xval = 0; - lval = ATOM_BYTES(atom); + /* FALL THROUGH */ + case JSOP_FORNAME2: + xval = NULL; + atom = NULL; + lval = ATOM_BYTES(GET_ATOM(cx, jp->script, pc)); sn = js_GetSrcNote(jp->script, pc); - pc += 3; + pc += oplen; goto do_forinloop; case JSOP_FORPROP: - atom = GET_ATOM(cx, jp->script, pc); rval = POP_STR(); - xval = 0; + /* FALL THROUGH */ + case JSOP_FORPROP2: + xval = NULL; + atom = GET_ATOM(cx, jp->script, pc); lval = POP_STR(); sn = NULL; - pc += 3; + pc += oplen; goto do_forinloop; case JSOP_FORELEM: rval = POP_STR(); + /* FALL THROUGH */ + case JSOP_FORELEM2: xval = POP_STR(); + atom = NULL; lval = POP_STR(); sn = NULL; pc++; do_forinloop: LOCAL_ASSERT(*pc == JSOP_IFEQ); + oplen = js_CodeSpec[JSOP_IFEQ].length; len = GET_JUMP_OFFSET(pc); js_printf(jp, "\tfor (%s%s", (sn && SN_TYPE(sn) == SRC_VAR) ? "var " : "", lval); - if (xval) + if (atom) + js_printf(jp, ".%s", ATOM_BYTES(atom)); + else if (xval) js_printf(jp, "[%s]", xval); + if (cs->format & JOF_FOR2) + rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]); js_printf(jp, " in %s) {\n", rval); jp->indent += 4; - DECOMPILE_CODE(pc + 3, len - 6); + DECOMPILE_CODE(pc + oplen, + len - (oplen + js_CodeSpec[JSOP_GOTO].length)); jp->indent -= 4; js_printf(jp, "\t}\n"); todo = -2; @@ -1080,21 +1215,24 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_SETARG: - atom = GetSlotAtom(jp->script->args, GET_ARGNO(pc)); + atom = GetSlotAtom(jp->scope, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_setname; case JSOP_SETVAR: - atom = GetSlotAtom(jp->script->vars, GET_VARNO(pc)); + atom = GetSlotAtom(jp->scope, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_setname; case JSOP_SETNAME: + case JSOP_SETNAME2: atom = GET_ATOM(cx, jp->script, pc); do_setname: lval = ATOM_BYTES(atom); rval = POP_STR(); - if ((sn = js_GetSrcNote(jp->script, pc - 1)) && + if (op == JSOP_SETNAME2) + (void) PopOff(ss, op); + if ((sn = js_GetSrcNote(jp->script, pc - 1)) != NULL && SN_TYPE(sn) == SRC_ASSIGNOP) { todo = Sprint(&ss->sprinter, "%s %s= %s", lval, js_CodeSpec[pc[-1]].token, rval); @@ -1193,13 +1331,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_INCARG: case JSOP_DECARG: - atom = GetSlotAtom(jp->script->args, GET_ARGNO(pc)); + atom = GetSlotAtom(jp->scope, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_incatom; case JSOP_INCVAR: case JSOP_DECVAR: - atom = GetSlotAtom(jp->script->vars, GET_VARNO(pc)); + atom = GetSlotAtom(jp->scope, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_incatom; @@ -1232,13 +1370,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_ARGINC: case JSOP_ARGDEC: - atom = GetSlotAtom(jp->script->args, GET_ARGNO(pc)); + atom = GetSlotAtom(jp->scope, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_atominc; case JSOP_VARINC: case JSOP_VARDEC: - atom = GetSlotAtom(jp->script->vars, GET_VARNO(pc)); + atom = GetSlotAtom(jp->scope, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_atominc; @@ -1284,7 +1422,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = POP_STR(); atom = GET_ATOM(cx, jp->script, pc); lval = POP_STR(); - if ((sn = js_GetSrcNote(jp->script, pc - 1)) && + if ((sn = js_GetSrcNote(jp->script, pc - 1)) != NULL && SN_TYPE(sn) == SRC_ASSIGNOP) { todo = Sprint(&ss->sprinter, "%s.%s %s= %s", lval, ATOM_BYTES(atom), @@ -1314,7 +1452,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) xval = POP_STR(); op = JSOP_SETELEM; lval = POP_STR(); - if ((sn = js_GetSrcNote(jp->script, pc - 1)) && + if ((sn = js_GetSrcNote(jp->script, pc - 1)) != NULL && SN_TYPE(sn) == SRC_ASSIGNOP) { todo = Sprint(&ss->sprinter, "%s[%s] %s= %s", lval, xval, @@ -1326,12 +1464,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_GETARG: - atom = GetSlotAtom(jp->script->args, GET_ARGNO(pc)); + atom = GetSlotAtom(jp->scope, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_name; case JSOP_GETVAR: - atom = GetSlotAtom(jp->script->vars, GET_VARNO(pc)); + atom = GetSlotAtom(jp->scope, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_name; @@ -1362,13 +1500,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) PR_cnvtf(buf, sizeof buf, 20, *JSVAL_TO_DOUBLE(key)); todo = Sprint(&ss->sprinter, buf); } else if (JSVAL_IS_STRING(key)) { - rval = EscapeString(&ss->sprinter, ATOM_TO_STRING(atom), - '"'); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), + '"'); if (!rval) return JS_FALSE; - todo = Sprint(&ss->sprinter, "\"%s\"", rval); + todo = Sprint(&ss->sprinter, "%s", rval); } else if (JSVAL_IS_OBJECT(key)) { - JSString *str = js_ObjectToString(cx, JSVAL_TO_OBJECT(key)); +#if JS_HAS_LEXICAL_CLOSURE + if (JSVAL_IS_FUNCTION(cx, key)) + goto do_closure; +#endif + str = js_ValueToString(cx, key); if (!str) return JS_FALSE; todo = SprintPut(&ss->sprinter, @@ -1388,24 +1530,25 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) TableEntry *table; sn = js_GetSrcNote(jp->script, pc); + PR_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); pc2 = pc; off = GET_JUMP_OFFSET(pc2); end = pc + off; - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); n = high - low + 1; table = JS_malloc(cx, (size_t)n * sizeof *table); if (!table) return JS_FALSE; - if (pc2 + 3 >= end) { + if (pc2 + JUMP_OFFSET_LEN + 1 >= end) { j = 0; } else { for (i = j = 0; i < n; i++) { - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; off2 = GET_JUMP_OFFSET(pc2); if (off2) { table[j].key = INT_TO_JSVAL(low + i); @@ -1431,19 +1574,21 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) TableEntry *table; sn = js_GetSrcNote(jp->script, pc); + PR_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); off = GET_JUMP_OFFSET(pc2); - pc2 += 2; + pc2 += JUMP_OFFSET_LEN; npairs = (jsint) GET_ATOM_INDEX(pc2); + pc2 += ATOM_INDEX_LEN; table = JS_malloc(cx, (size_t)npairs * sizeof *table); if (!table) return JS_FALSE; for (i = 0; i < npairs; i++) { - pc2 += 2; atom = GET_ATOM(cx, jp->script, pc2); - pc2 += 2; + pc2 += ATOM_INDEX_LEN; off2 = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; table[i].key = ATOM_KEY(atom); table[i].offset = off2; } @@ -1479,17 +1624,16 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) JSObject *obj; JSFunction *fun; JSPrinter *jp2; - JSString *str; atom = GET_ATOM(cx, jp->script, pc); PR_ASSERT(ATOM_IS_OBJECT(atom)); + do_closure: obj = ATOM_TO_OBJECT(atom); fun = JS_GetPrivate(cx, obj); - jp2 = js_NewPrinter(cx, - fun->atom ? ATOM_BYTES(fun->atom) : NULL, - jp->indent); + jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun), jp->indent); if (!jp2) return JS_FALSE; + jp2->scope = jp->scope; todo = -1; if (js_DecompileFunction(jp2, fun, JS_FALSE)) { str = js_GetPrinterOutput(jp2); @@ -1550,7 +1694,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = -2; break; -#ifdef JS_HAS_OBJECT_LITERAL +#ifdef JS_HAS_INITIALIZERS case JSOP_NEWINIT: LOCAL_ASSERT(ss->top >= 2); (void) PopOff(ss, op); @@ -1609,11 +1753,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; #if JS_HAS_SHARP_VARS + case JSOP_DEFSHARP: + i = (jsint) GET_ATOM_INDEX(pc); + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval); + break; + case JSOP_USESHARP: i = (jsint) GET_ATOM_INDEX(pc); todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i); + break; #endif /* JS_HAS_SHARP_VARS */ -#endif /* JS_HAS_OBJECT_LITERAL */ +#endif /* JS_HAS_INITIALIZERS */ default: todo = -2; @@ -1622,6 +1773,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } if (todo < 0) { + /* -2 means "don't push", -1 means reported error. */ if (todo == -1) return JS_FALSE; } else { @@ -1692,39 +1844,50 @@ js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len) JSBool js_DecompileScript(JSPrinter *jp, JSScript *script) { - PR_ASSERT(JS_IS_LOCKED(jp->sprinter.context)); return js_DecompileCode(jp, script, script->code, (uintN)script->length); } JSBool js_DecompileFunction(JSPrinter *jp, JSFunction *fun, JSBool newlines) { - JSSymbol *arg; + JSScope *scope, *oldscope; + JSScopeProperty *sprop, *snext; + JSBool more, ok; JSAtom *atom; uintN indent; - PR_ASSERT(JS_IS_LOCKED(jp->sprinter.context)); if (newlines) { js_puts(jp, "\n"); js_printf(jp, "\t"); } js_printf(jp, "function %s(", fun->atom ? ATOM_BYTES(fun->atom) : ""); - if (fun->script) { - for (arg = fun->script->args; arg; arg = arg->next) { - atom = sym_atom(arg); - js_printf(jp, "%s%s", ATOM_BYTES(atom), arg->next ? ", " : ""); + if (fun->script && fun->object) { + scope = (JSScope *)fun->object->map; + for (sprop = scope->props; sprop; sprop = snext) { + snext = sprop->next; + if (sprop->getter != js_GetArgument) + continue; + more = (snext && snext->getter == js_GetArgument); + atom = sym_atom(sprop->symbols); + js_printf(jp, "%s%s", ATOM_BYTES(atom), more ? ", " : ""); + if (!more) + break; } } js_puts(jp, ") {\n"); indent = jp->indent; jp->indent += 4; - if (fun->call) { - js_printf(jp, "\t[native code]\n"); - } else { - if (!js_DecompileScript(jp, fun->script)) { + if (fun->script) { + oldscope = jp->scope; + jp->scope = scope; + ok = js_DecompileScript(jp, fun->script); + jp->scope = oldscope; + if (!ok) { jp->indent = indent; return JS_FALSE; } + } else { + js_printf(jp, "\t[native code]\n"); } jp->indent -= 4; js_printf(jp, "\t}"); @@ -1734,76 +1897,108 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun, JSBool newlines) } JSString * -js_ValueToSource(JSContext *cx, jsval v) +js_DecompileValueGenerator(JSContext *cx, jsval v, JSString *fallback) { JSStackFrame *fp; - JSScript *script; jsbytecode *pc, *begin, *end, *tmp; + jsval *sp, *base, *limit; + JSScript *script; + JSCodeSpec *cs; uint32 format, mode; intN depth; - jsval *limit; jssrcnote *sn; - uintN len; + uintN len, maxoplen; JSPrinter *jp; JSString *name; fp = cx->fp; - script = fp->script; - if (!script) - return JS_ValueToString(cx, v); + if (!fp) + goto do_fallback; + /* Try to find sp's generating pc depth slots under it on the stack. */ pc = fp->pc; - format = js_CodeSpec[*pc].format; - mode = (format & JOF_MODEMASK); - - /* Don't look on the stack for element ops, the index will throw us. */ - if (mode != JOF_ELEM) { - limit = (jsval *)cx->stackPool.current->avail; - depth = (intN)script->depth; - if (fp->sp < limit && fp->sp[0] == v) - pc = (jsbytecode *)fp->sp[0 - depth]; - if (!pc && fp->sp + 1 < limit && fp->sp[1] == v) - pc = (jsbytecode *)fp->sp[1 - depth]; - - if (pc != fp->pc) { - if ((pruword)(pc - script->code) >= (pruword)script->length) { - pc = fp->pc; - } else { - format = js_CodeSpec[*pc].format; - mode = (format & JOF_MODEMASK); + limit = (jsval *) cx->stackPool.current->avail; + if (!pc && + fp->argv && + fp->down && + (script = fp->down->script) != NULL) { + /* + * Native frame called by script: try to match v with actual argument. + * If (fp->sp < fp->argv), normally an impossibility, then we are in + * js_ReportIsNotFunction and sp points to the offending non-function + * on the stack. + */ + for (sp = (fp->sp < fp->argv) ? fp->sp : fp->argv; sp < limit; sp++) { + if (*sp == v) { + depth = (intN)script->depth; + pc = (jsbytecode *) sp[-depth]; + break; } } + } else { + /* Could be native frame called by native code, so check script. */ + script = fp->script; + if (!script) + goto do_fallback; + + /* OK, interpreted frame. Try the operand at sp, or one above it. */ + sp = fp->sp; + if (sp[0] != v && sp + 1 < limit && sp[1] == v) + sp++; + + /* Try to find an operand-generating pc just above fp's variables. */ + depth = (intN)script->depth; + base = fp->vars + ? fp->vars + fp->nvars + : (jsval *) cx->stackPool.current->base; + if (PR_UPTRDIFF(sp - depth, base) < PR_UPTRDIFF(limit, base)) + pc = (jsbytecode *) sp[-depth]; } - if (mode != JOF_NAME && - (!(sn = js_GetSrcNote(script, pc)) || - SN_TYPE(sn) != SRC_PCBASE)) { - return JS_ValueToString(cx, v); + /* Be paranoid about loading an invalid pc from sp[-depth]. */ + if (!pc) + goto do_fallback; + PR_ASSERT(PR_UPTRDIFF(pc, script->code) < (pruword)script->length); + if (PR_UPTRDIFF(pc, script->code) >= (pruword)script->length) { + pc = fp->pc; + if (!pc) + goto do_fallback; } + cs = &js_CodeSpec[*pc]; + format = cs->format; + mode = (format & JOF_MODEMASK); + /* NAME ops are self-contained, but others require left context. */ if (mode == JOF_NAME) { begin = pc; - end = pc + 3; } else { + sn = js_GetSrcNote(script, pc); + if (!sn || SN_TYPE(sn) != SRC_PCBASE) + goto do_fallback; begin = pc - js_GetSrcNoteOffset(sn, 0); - end = fp->pc; } + end = pc + cs->length; len = end - begin; if (format & (JOF_SET | JOF_DEL | JOF_INCDEC | JOF_IMPORT)) { - /* These ops require bytecode source extension. */ - tmp = JS_malloc(cx, (len + 3) * sizeof(jsbytecode)); + /* These formats require bytecode source extension. */ + maxoplen = js_CodeSpec[JSOP_GETPROP].length; + tmp = JS_malloc(cx, (len + maxoplen) * sizeof(jsbytecode)); if (!tmp) return NULL; memcpy(tmp, begin, len * sizeof(jsbytecode)); if (mode == JOF_NAME) { tmp[0] = JSOP_NAME; - } else if (mode == JOF_PROP) { - tmp[len++] = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP; - tmp[len++] = pc[1]; - tmp[len++] = pc[2]; } else { - tmp[len++] = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM; + if (begin[0] == JSOP_TRAP) + tmp[0] = JS_GetTrapOpcode(cx, script, begin); + if (mode == JOF_PROP) { + tmp[len++] = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP; + tmp[len++] = pc[1]; + tmp[len++] = pc[2]; + } else { + tmp[len++] = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM; + } } begin = tmp; } else { @@ -1811,15 +2006,16 @@ js_ValueToSource(JSContext *cx, jsval v) tmp = NULL; } - JS_LOCK(cx); - jp = js_NewPrinter(cx, "js_ValueToSource", 0); + jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0); if (jp && js_DecompileCode(jp, script, begin, len)) name = js_GetPrinterOutput(jp); else name = NULL; js_DestroyPrinter(jp); - JS_UNLOCK(cx); if (tmp) JS_free(cx, tmp); return name; + + do_fallback: + return fallback ? fallback : js_ValueToString(cx, v); } diff --git a/mozilla/js/src/jsopcode.def b/mozilla/js/src/jsopcode.def index 1b612d45839..3733aa6268e 100644 --- a/mozilla/js/src/jsopcode.def +++ b/mozilla/js/src/jsopcode.def @@ -15,7 +15,6 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ - /* * JavaScript operation bytecodes. * @@ -84,7 +83,7 @@ OPDEF(JSOP_NEG, 34, "neg", "-", 1, 1, 1, 10, JOF_BYTE) OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 10, JOF_UINT16) OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEL) OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_DEL) -OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_ELEM|JOF_DEL) +OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_DEL) OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 10, JOF_BYTE) OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 10, JOF_BYTE) OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC) @@ -165,12 +164,29 @@ OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 3, 0, 0, 0, JOF_UINT16 OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 3, 0, 1, 0, JOF_UINT16) /* Fast inc/dec ops for args and local vars. */ -OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 10, JOF_QARG |JOF_INC) -OPDEF(JSOP_INCVAR, 96, "incvar", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_INC) -OPDEF(JSOP_DECARG, 97, "decarg", NULL, 3, 0, 1, 10, JOF_QARG |JOF_DEC) -OPDEF(JSOP_DECVAR, 98, "decvar", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_DEC) -OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 10, JOF_QARG |JOF_INC|JOF_POST) -OPDEF(JSOP_VARINC, 100,"varinc", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_INC|JOF_POST) -OPDEF(JSOP_ARGDEC, 101,"argdec", NULL, 3, 0, 1, 10, JOF_QARG |JOF_DEC|JOF_POST) -OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_DEC|JOF_POST) +OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_INC) +OPDEF(JSOP_INCVAR, 96, "incvar", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_INC) +OPDEF(JSOP_DECARG, 97, "decarg", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_DEC) +OPDEF(JSOP_DECVAR, 98, "decvar", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_DEC) +OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_VARINC, 100,"varinc", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_ARGDEC, 101,"argdec", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST) +OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_DEC|JOF_POST) +/* ECMA-compliant for/in ops. */ +OPDEF(JSOP_TOOBJECT, 103,"toobject", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_FORNAME2, 104,"forname2", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_SET|JOF_FOR2) +OPDEF(JSOP_FORPROP2, 105,"forprop2", NULL, 3, 1, 1, 0, JOF_CONST|JOF_PROP|JOF_SET|JOF_FOR2) +OPDEF(JSOP_FORELEM2, 106,"forelem2", NULL, 1, 2, 1, 0, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_FOR2) +OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE) + +/* ECMA-complaint assignment ops. */ +OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME) +OPDEF(JSOP_SETNAME2, 109,"setname2", NULL, 3, 2, 1, 1, JOF_CONST|JOF_NAME|JOF_SET) + +/* Exception handling ops. */ +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) diff --git a/mozilla/js/src/jsopcode.h b/mozilla/js/src/jsopcode.h index 551f0b0f7ff..d00f1d88b57 100644 --- a/mozilla/js/src/jsopcode.h +++ b/mozilla/js/src/jsopcode.h @@ -61,10 +61,12 @@ typedef enum JSOp { #define JOF_INCDEC 0x0300 /* increment or decrement opcode */ #define JOF_POST 0x0400 /* postorder increment or decrement */ #define JOF_IMPORT 0x0800 /* import property op */ +#define JOF_FOR2 0x1000 /* new for/in loop bytecodes */ /* * Immediate operand getters, setters, and bounds. */ +#define JUMP_OFFSET_LEN 2 #define JUMP_OFFSET_HI(off) ((jsbytecode)((off) >> 8)) #define JUMP_OFFSET_LO(off) ((jsbytecode)(off)) #define GET_JUMP_OFFSET(pc) ((int16)(((pc)[1] << 8) | (pc)[2])) @@ -73,11 +75,12 @@ typedef enum JSOp { #define JUMP_OFFSET_MIN ((int16)0x8000) #define JUMP_OFFSET_MAX ((int16)0x7fff) +#define ATOM_INDEX_LEN 2 #define ATOM_INDEX_HI(index) ((jsbytecode)((index) >> 8)) #define ATOM_INDEX_LO(index) ((jsbytecode)(index)) #define GET_ATOM_INDEX(pc) (((pc)[1] << 8) | (pc)[2]) -#define SET_ATOM_INDEX(pc,ndx) ((pc)[1] = ATOM_INDEX_HI(ndx), \ - (pc)[2] = ATOM_INDEX_LO(ndx)) +#define SET_ATOM_INDEX(pc,index)((pc)[1] = ATOM_INDEX_HI(index), \ + (pc)[2] = ATOM_INDEX_LO(index)) #define GET_ATOM(cx,script,pc) js_GetAtom((cx), &(script)->atomMap, \ GET_ATOM_INDEX(pc)) #define ATOM_INDEX_LIMIT_LOG2 16 @@ -104,6 +107,8 @@ struct JSCodeSpec { uint32 format; /* immediate operand format */ }; +extern char js_in_str[]; +extern char js_instanceof_str[]; extern char js_new_str[]; extern char js_delete_str[]; extern char js_typeof_str[]; @@ -118,10 +123,11 @@ extern jschar js_EscapeMap[]; /* * Return a GC'ed string containing the chars in str, with any non-printing - * chars or quotes (' or " as specified by the quote argument) escaped. + * chars or quotes (' or " as specified by the quote argument) escaped, and + * with the quote character at the beginning and end of the result string. */ extern JSString * -js_EscapeString(JSContext *cx, JSString *str, jschar quote); +js_QuoteString(JSContext *cx, JSString *str, jschar quote); /* * JSPrinter operations, for printf style message formatting. The return @@ -149,10 +155,10 @@ js_puts(JSPrinter *jp, char *s); */ #include -extern void +extern JS_FRIEND_API(void) js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp); -extern uintN +extern JS_FRIEND_API(uintN) js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, JSBool lines, FILE *fp); #endif /* DEBUG */ @@ -171,11 +177,11 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun, JSBool newlines); /* * Find the source expression that resulted in v, and return a new string - * containing it. Fall back on v's string conversion if cx lacks sufficient - * information to tell what source resulted in v. + * containing it. Fall back on v's string conversion if we can't find the + * bytecode that generated and pushed v on the operand stack. */ extern JSString * -js_ValueToSource(JSContext *cx, jsval v); +js_DecompileValueGenerator(JSContext *cx, jsval v, JSString *fallback); PR_END_EXTERN_C diff --git a/mozilla/js/src/jsparse.c b/mozilla/js/src/jsparse.c index b7644c80ce9..b814c33486e 100644 --- a/mozilla/js/src/jsparse.c +++ b/mozilla/js/src/jsparse.c @@ -21,9 +21,11 @@ * * This is a recursive-descent parser for the JavaScript language specified by * "The JavaScript 1.2 Language Specification". It uses lexical and semantic - * feedback to disambiguate non-LL(1) structures. It generates bytecode as it - * parses, with backpatching and code rewriting limited to downward branches, - * for loops, and assignable "lvalue" expressions. + * feedback to disambiguate non-LL(1) structures. It generates trees of nodes + * induced by the recursive parsing (not precise syntax trees, see jsparse.h). + * After tree construction, it rewrites trees to fold constants and evaluate + * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to + * generate bytecode. * * This parser attempts no error recovery. The dense JSTokenType enumeration * was designed with error recovery built on 64-bit first and follow bitsets @@ -31,6 +33,7 @@ */ #include #include +#include #include "prtypes.h" #ifndef NSPR20 #include "prarena.h" @@ -62,8 +65,8 @@ * Each parser takes a context and a token stream, and emits bytecode using * a code generator. */ -typedef JSBool -JSParser(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg); +typedef JSParseNode * +JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); static JSParser FunctionStmt; #if JS_HAS_LEXICAL_CLOSURE @@ -90,44 +93,93 @@ static JSParser MemberExpr; static JSParser PrimaryExpr; /* - * Insist that the next token be of type tt, or report err and return false. - * NB: this macro uses cx, ts, and cg from its lexical environment. + * Insist that the next token be of type tt, or report err and throw or fail. + * NB: this macro uses cx and ts from its lexical environment. */ #define MUST_MATCH_TOKEN_THROW(tt, err, throw) \ PR_BEGIN_MACRO \ - if (js_GetToken(cx, ts, cg) != tt) { \ + if (js_GetToken(cx, ts) != tt) { \ js_ReportCompileError(cx, ts, err); \ throw; \ } \ PR_END_MACRO #define MUST_MATCH_TOKEN(tt, err) \ - MUST_MATCH_TOKEN_THROW(tt, err, return JS_FALSE) + MUST_MATCH_TOKEN_THROW(tt, err, return NULL) /* - * Emit a bytecode and its 2-byte constant (atom) index immediate operand. - * NB: We use cx and cg from our caller's lexical environment, and return - * false on error. + * Allocate a JSParseNode from cx's temporary arena. */ -#define EMIT_CONST_ATOM_OP(op, atomIndex) \ - PR_BEGIN_MACRO \ - if (js_Emit3(cx, cg, op, ATOM_INDEX_HI(atomIndex), \ - ATOM_INDEX_LO(atomIndex)) < 0) { \ - return JS_FALSE; \ - } \ - PR_END_MACRO +static JSParseNode * +NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity) +{ + JSParseNode *pn; + + PR_ARENA_ALLOCATE(pn, &cx->tempPool, sizeof(JSParseNode)); + if (!pn) + return NULL; + pn->pn_type = tok->type; + pn->pn_pos = tok->pos; + pn->pn_arity = arity; + pn->pn_next = NULL; + return pn; +} + +static JSParseNode * +NewBinary(JSContext *cx, JSTokenType tt, + JSOp op, JSParseNode *left, JSParseNode *right) +{ + JSParseNode *pn; + + if (!left || !right) + return NULL; + PR_ARENA_ALLOCATE(pn, &cx->tempPool, sizeof(JSParseNode)); + if (!pn) + return NULL; + pn->pn_type = tt; + pn->pn_pos.begin = left->pn_pos.begin; + pn->pn_pos.end = right->pn_pos.end; + pn->pn_op = op; + pn->pn_arity = PN_BINARY; + pn->pn_left = left; + pn->pn_right = right; + pn->pn_next = NULL; + return pn; +} + +static JSBool +WellTerminated(JSContext *cx, JSTokenStream *ts, JSTokenType lastExprType) +{ + JSTokenType tt; + + tt = js_PeekTokenSameLine(cx, ts); + if (tt == TOK_ERROR) + return JS_FALSE; + 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) { + return JS_TRUE; + } +#endif + js_ReportCompileError(cx, ts, "missing ; before statement"); + return JS_FALSE; + } + return JS_TRUE; +} /* * Parse a top-level JS script. */ JS_FRIEND_API(JSBool) -js_Parse(JSContext *cx, JSObject *chain, JSTokenStream *ts, JSCodeGenerator *cg) +js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, + JSCodeGenerator *cg) { JSStackFrame *fp, frame; JSTokenType stop, tt; JSBool ok; + JSParseNode *pn; - PR_ASSERT(JS_IS_LOCKED(cx)); fp = cx->fp; if (!fp || fp->scopeChain != chain) { memset(&frame, 0, sizeof frame); @@ -146,7 +198,7 @@ js_Parse(JSContext *cx, JSObject *chain, JSTokenStream *ts, JSCodeGenerator *cg) ok = JS_TRUE; do { ts->flags |= TSF_REGEXP; - tt = js_GetToken(cx, ts, cg); + tt = js_GetToken(cx, ts); ts->flags &= ~TSF_REGEXP; if (tt == stop || tt <= TOK_EOF) { if (tt == TOK_ERROR) @@ -156,45 +208,89 @@ js_Parse(JSContext *cx, JSObject *chain, JSTokenStream *ts, JSCodeGenerator *cg) switch (tt) { case TOK_FUNCTION: - if (!FunctionStmt(cx, ts, cg)) - ok = JS_FALSE; + pn = FunctionStmt(cx, ts, &cg->treeContext); + if (pn && pn->pn_pos.end.lineno == ts->lineno) { + ok = WellTerminated(cx, ts, TOK_FUNCTION); + if (!ok) + goto out; + } break; + default: js_UngetToken(ts); - if (!Statement(cx, ts, cg)) - ok = JS_FALSE; + pn = Statement(cx, ts, &cg->treeContext); + if (pn) { + ok = js_FoldConstants(cx, pn); + if (!ok) + goto out; + } + break; + } + if (pn) { + ok = js_AllocTryNotes(cx, cg); + if (ok) + ok = js_EmitTree(cx, cg, pn); + } else { + ok = JS_FALSE; } } while (ok); +out: cx->fp = fp; - if (ok) - ok = js_FlushNewlines(cx, ts, cg); - if (!ok) { + if (!ok) CLEAR_PUSHBACK(ts); - js_DropUnmappedAtoms(cx, &cg->atomList); - } return ok; } /* - * Parse a JS function body, which might appear as the value of an event - * handler attribute in a HTML tag. + * 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 + * that contains an early return e1 will get an error XXX should be warning + * option). */ -JSBool -js_ParseFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, - JSSymbol *args) +static JSBool +CheckFinalReturn(JSParseNode *pn) { - uintN lineno, oldflags; - JSCodeGenerator funcg; - JSStackFrame *fp, frame; JSBool ok; - jsbytecode *pc; + JSParseNode *pn2, *pn3; - PR_ASSERT(JS_IS_LOCKED(cx)); + switch (pn->pn_type) { + case TOK_LC: + if (!pn->pn_head) + return JS_FALSE; + return CheckFinalReturn(PN_LAST(pn)); + case TOK_IF: + ok = CheckFinalReturn(pn->pn_kid2); + ok &= pn->pn_kid3 && CheckFinalReturn(pn->pn_kid3); + return ok; + case TOK_SWITCH: + /* If a final switch has no default case, we judge it harshly. */ + ok = pn->pn_kid3 && CheckFinalReturn(pn->pn_kid3); + for (pn2 = pn->pn_kid2->pn_head; ok && pn2; pn2 = pn2->pn_next) { + pn3 = pn2->pn_right; + PR_ASSERT(pn3->pn_type == TOK_LC); + if (pn3->pn_head) + ok &= CheckFinalReturn(PN_LAST(pn3)); + } + return ok; + case TOK_WITH: + return CheckFinalReturn(pn->pn_right); + case TOK_RETURN: + return JS_TRUE; + default: + return JS_FALSE; + } +} - lineno = ts->lineno; - if (!js_InitCodeGenerator(cx, &funcg, &cx->codePool)) - return JS_FALSE; +static char badreturn_str[] = "function does not always return a value"; + +static JSParseNode * +FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, + JSTreeContext *tc) +{ + JSStackFrame *fp, frame; + uintN oldflags; + JSParseNode *pn; fp = cx->fp; if (!fp || fp->scopeChain != fun->object) { @@ -204,91 +300,106 @@ js_ParseFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, cx->fp = &frame; } - oldflags = ts->flags; - ts->flags &= ~(TSF_RETURN_EXPR | TSF_RETURN_VOID); - ts->flags |= TSF_FUNCTION; - ok = Statements(cx, ts, &funcg); + oldflags = tc->flags; + tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); + tc->flags |= TCF_IN_FUNCTION; + pn = Statements(cx, ts, tc); /* Check for falling off the end of a function that returns a value. */ - if (ok && (ts->flags & TSF_RETURN_EXPR)) { - for (pc = CG_CODE(&funcg,funcg.lastCodeOffset); - *pc == JSOP_NOP || *pc == JSOP_LEAVEWITH; - pc--) { - /* nothing */; - } - if (*pc != JSOP_RETURN) { - js_ReportCompileError(cx, ts, - "function does not always return a value"); - ok = JS_FALSE; + if (pn && (tc->flags & TCF_RETURN_EXPR)) { + if (!CheckFinalReturn(pn)) { + js_ReportCompileError(cx, ts, badreturn_str); + pn = NULL; } } - ts->flags = oldflags; - cx->fp = fp; - if (ok) - ok = js_FlushNewlines(cx, ts, &funcg); - if (!ok) { - CLEAR_PUSHBACK(ts); - js_DropUnmappedAtoms(cx, &funcg.atomList); - } else { - funcg.args = args; - fun->script = js_NewScript(cx, &funcg, ts->filename, lineno, - ts->principals, fun); - if (!fun->script) - ok = JS_FALSE; + cx->fp = fp; + tc->flags = oldflags; + return pn; +} + +/* + * Compile a JS function body, which might appear as the value of an event + * handler attribute in an HTML tag. + */ +JSBool +js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun) +{ + JSCodeGenerator funcg; + JSParseNode *pn; + JSBool ok; + + if (!js_InitCodeGenerator(cx, &funcg, ts->filename, ts->lineno, + ts->principals)) { + return JS_FALSE; } + + /* Satisfy the assertion at the top of Statements. */ + ts->token.type = TOK_LC; + pn = FunctionBody(cx, ts, fun, &funcg.treeContext); + if (!pn) { + CLEAR_PUSHBACK(ts); + ok = JS_FALSE; + } else { + ok = js_FoldConstants(cx, pn); + if (ok) + ok = js_EmitFunctionBody(cx, &funcg, pn, fun); + } + js_ResetCodeGenerator(cx, &funcg); return ok; } -#if JS_HAS_LEXICAL_CLOSURE static JSBool -InWithStatement(JSCodeGenerator *cg) +InWithStatement(JSTreeContext *tc) { - StmtInfo *stmt; + JSStmtInfo *stmt; - for (stmt = cg->stmtInfo; stmt; stmt = stmt->down) { + for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (stmt->type == STMT_WITH) return JS_TRUE; } return JS_FALSE; } -#endif -static JSBool -FunctionDef(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, JSBool expr) +static JSParseNode * +FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool lambda) { - JSAtomMap map; + JSParseNode *pn, *pn2; JSAtom *funAtom, *argAtom; JSObject *parent; JSFunction *fun, *outerFun; + JSStmtInfo *topStmt; JSBool ok, named; - JSSymbol *arg, *args, **argp; JSObject *pobj; - JSProperty *prop; - void *mark; + JSScopeProperty *sprop; jsval junk; - uint32 i; - /* Save atoms indexed but not mapped for the top-level script. */ - if (!js_InitAtomMap(cx, &map, &cg->atomList)) - return JS_FALSE; + /* Make a TOK_FUNCTION node. */ + pn = NewParseNode(cx, &ts->token, PN_FUNC); + if (!pn) + return NULL; - if (js_MatchToken(cx, ts, cg, TOK_NAME)) - funAtom = js_HoldAtom(cx, ts->token.u.atom); + /* Scan the optional function name into funAtom. */ + if (js_MatchToken(cx, ts, TOK_NAME)) + funAtom = ts->token.t_atom; else funAtom = NULL; /* Find the nearest variable-declaring scope and use it as our parent. */ parent = js_FindVariableScope(cx, &outerFun); - if (!parent) { - ok = JS_FALSE; - goto out; - } + 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 || InWithStatement(cg) || cx->fp->scopeChain != parent) { + if (!funAtom || cx->fp->scopeChain != parent || InWithStatement(tc)) { /* Don't name the function if enclosed by a with statement or equiv. */ - fun = js_NewFunction(cx, NULL, 0, 0, cx->fp->scopeChain, funAtom); + fun = js_NewFunction(cx, NULL, NULL, 0, 0, cx->fp->scopeChain, + funAtom); named = JS_FALSE; } else #endif @@ -303,48 +414,50 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, JSBool expr) } /* Now parse formal argument list and compute fun->nargs. */ - args = NULL; MUST_MATCH_TOKEN_THROW(TOK_LP, "missing ( before formal parameters", ok = JS_FALSE; goto out); /* balance) */ - if (!js_MatchToken(cx, ts, cg, TOK_RP)) { - argp = &args; + if (!js_MatchToken(cx, ts, TOK_RP)) { do { MUST_MATCH_TOKEN_THROW(TOK_NAME, "missing formal parameter", ok = JS_FALSE; goto out); - argAtom = js_HoldAtom(cx, ts->token.u.atom); + argAtom = ts->token.t_atom; pobj = NULL; - ok = js_LookupProperty(cx, fun->object, (jsval)argAtom, &pobj, - &prop); + ok = js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj, + (JSProperty **)&sprop); if (!ok) goto out; - if (prop && prop->object == fun->object) { - if (prop->getter == js_GetArgument) { + if (sprop && pobj == fun->object) { +#ifdef CHECK_ARGUMENT_HIDING + if (sprop->getter == js_GetArgument) { + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); js_ReportCompileError(cx, ts, "duplicate formal argument %s", ATOM_BYTES(argAtom)); ok = JS_FALSE; goto out; } - prop->getter = js_GetArgument; - prop->setter = js_SetArgument; - prop->flags |= JSPROP_ENUMERATE | JSPROP_PERMANENT; +#endif + sprop->getter = js_GetArgument; + sprop->setter = js_SetArgument; + sprop->attrs |= JSPROP_ENUMERATE | JSPROP_PERMANENT; } else { - prop = js_DefineProperty(cx, fun->object, - (jsval)argAtom, JSVAL_VOID, - js_GetArgument, js_SetArgument, - JSPROP_ENUMERATE | JSPROP_PERMANENT); + if (sprop) { + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + sprop = NULL; + } + ok = js_DefineProperty(cx, fun->object, + (jsid)argAtom, JSVAL_VOID, + js_GetArgument, js_SetArgument, + JSPROP_ENUMERATE | JSPROP_PERMANENT, + (JSProperty **)&sprop); } - js_DropAtom(cx, argAtom); - if (!prop) { - ok = JS_FALSE; + if (!ok) goto out; - } - prop->id = INT_TO_JSVAL(fun->nargs++); - arg = prop->symbols; - *argp = arg; - argp = &arg->next; - } while (js_MatchToken(cx, ts, cg, TOK_COMMA)); + PR_ASSERT(sprop); + sprop->id = INT_TO_JSVAL(fun->nargs++); + OBJ_DROP_PROPERTY(cx, fun->object, (JSProperty *)sprop); + } while (js_MatchToken(cx, ts, TOK_COMMA)); /* (balance: */ MUST_MATCH_TOKEN_THROW(TOK_RP, "missing ) after formal parameters", @@ -353,880 +466,761 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, JSBool expr) MUST_MATCH_TOKEN_THROW(TOK_LC, "missing { before function body", ok = JS_FALSE; goto out); - mark = PR_ARENA_MARK(&cx->codePool); - ok = js_ParseFunctionBody(cx, ts, fun, args); - if (ok) { - MUST_MATCH_TOKEN_THROW(TOK_RC, "missing } after function body", - ok = JS_FALSE; goto out); - fun->script->depth += fun->nvars; + pn->pn_pos.begin = ts->token.pos.begin; - /* Generate a setline note for script that follows this function. */ - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)TRUE_LINENO(ts)) < 0) - ok = JS_FALSE; + pn2 = FunctionBody(cx, ts, fun, tc); + if (!pn2) { + ok = JS_FALSE; + goto out; } - PR_ARENA_RELEASE(&cx->codePool, mark); + MUST_MATCH_TOKEN_THROW(TOK_RC, "missing } after function body", + ok = JS_FALSE; goto out); + pn->pn_pos.end = ts->token.pos.end; + + pn->pn_fun = fun; + pn->pn_body = pn2; + pn->pn_tryCount = tc->tryCount; + +#if JS_HAS_LEXICAL_CLOSURE + if (outerFun || cx->fp->scopeChain != parent || InWithStatement(tc)) + pn->pn_op = JSOP_CLOSURE; + else if (lambda) + pn->pn_op = JSOP_OBJECT; + else +#endif + pn->pn_op = JSOP_NOP; + + ok = JS_TRUE; out: - if (!ok && named) - (void) js_DeleteProperty(cx, parent, (jsval)funAtom, &junk); - for (i = 0; i < map.length; i++) { - js_IndexAtom(cx, map.vector[i], &cg->atomList); - PR_ASSERT(map.vector[i]->index == i); + tc->topStmt = topStmt; + if (!ok) { + if (named) + (void) OBJ_DELETE_PROPERTY(cx, parent, (jsid)funAtom, &junk); + return NULL; } - js_FreeAtomMap(cx, &map); - if (funAtom) - js_DropAtom(cx, funAtom); - -#if JS_HAS_LEXICAL_CLOSURE - if (fun) { - JSBool closure = (outerFun || - InWithStatement(cg) || - cx->fp->scopeChain != parent); - - if (closure || expr) { - jsatomid atomIndex; - - /* Make the function object a literal in the outer script's pool. */ - funAtom = js_AtomizeObject(cx, fun->object, ATOM_NOHOLD); - if (!funAtom) - return JS_FALSE; - - /* Emit a closure or push naming the literal in its immediate. */ - atomIndex = js_IndexAtom(cx, funAtom, &cg->atomList); - EMIT_CONST_ATOM_OP(closure ? JSOP_CLOSURE : JSOP_OBJECT, atomIndex); - } - } -#endif - return ok; + return pn; } -static JSBool -FunctionStmt(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - return FunctionDef(cx, ts, cg, JS_FALSE); + return FunctionDef(cx, ts, tc, JS_FALSE); } #if JS_HAS_LEXICAL_CLOSURE -static JSBool -FunctionExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - return FunctionDef(cx, ts, cg, JS_TRUE); + return FunctionDef(cx, ts, tc, JS_TRUE); } #endif -static JSBool -Statements(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +/* + * Parse the statements in a block, creating a TOK_LC node that lists the + * statements' trees. Our caller must match { before and } after. + */ +static JSParseNode * +Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { + JSParseNode *pn, *pn2; uintN newlines; - JSBool ok; JSTokenType tt; + PR_ASSERT(ts->token.type == TOK_LC); + pn = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn) + return NULL; + PN_INIT_LIST(pn); + newlines = ts->flags & TSF_NEWLINES; if (newlines) HIDE_NEWLINES(ts); - ok = JS_TRUE; - while ((tt = js_PeekToken(cx, ts, cg)) > TOK_EOF && tt != TOK_RC) { - ok = Statement(cx, ts, cg); - if (!ok) - break; + while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { + pn2 = Statement(cx, ts, tc); + if (!pn2) { + pn = NULL; + goto out; + } + PN_APPEND(pn, pn2); } + + pn->pn_pos.end = ts->token.pos.end; if (tt == TOK_ERROR) - ok = JS_FALSE; + pn = NULL; +out: if (newlines) SCAN_NEWLINES(ts); - return ok; + return pn; } -static JSBool -Condition(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - jsbytecode *pc; - JSOp lastop; - jsatomid atomIndex; + JSParseNode *pn, *pn2; MUST_MATCH_TOKEN(TOK_LP, "missing ( before condition"); - if (!Expr(cx, ts, cg)) - return JS_FALSE; + pn = Expr(cx, ts, tc); + if (!pn) + return NULL; MUST_MATCH_TOKEN(TOK_RP, "missing ) after condition"); /* - * Check for an AssignExpr (see below) and "correct" it to an EqExpr. + * Check for (a = b) and "correct" it to (a == b). + * XXX not ECMA, but documented in several books -- need a compile option */ - pc = CG_CODE(cg, cg->lastCodeOffset); - lastop = (JSOp) *pc; - if (lastop == JSOP_SETNAME && - (cg->noteCount == 0 || - cg->lastNoteOffset != cg->lastCodeOffset - 1 || - SN_TYPE(&cg->notes[cg->saveNoteCount]) != SRC_ASSIGNOP)) { + if (pn->pn_type == TOK_ASSIGN && pn->pn_op == JSOP_NOP) { js_ReportCompileError(cx, ts, "test for equality (==) mistyped as assignment (=)?\n" "Assuming equality test"); - atomIndex = GET_ATOM_INDEX(pc); - js_CancelLastOpcode(cx, cg, &ts->newlines); - EMIT_CONST_ATOM_OP(JSOP_NAME, atomIndex); - if (js_Emit1(cx, cg, cx->jsop_eq) < 0) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -WellTerminated(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, - JSTokenType lastExprType) -{ - JSTokenType tt; - - tt = ts->pushback.type; - if (tt == TOK_ERROR) - return JS_FALSE; - 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) { - return JS_TRUE; + pn->pn_type = TOK_EQOP; + pn->pn_op = cx->jsop_eq; + pn2 = pn->pn_left; + switch (pn2->pn_op) { + case JSOP_SETARG: + pn2->pn_op = JSOP_GETARG; + break; + case JSOP_SETVAR: + pn2->pn_op = JSOP_GETVAR; + break; + case JSOP_SETNAME2: + pn2->pn_op = JSOP_NAME; + break; + case JSOP_SETPROP: + pn2->pn_op = JSOP_GETPROP; + break; + case JSOP_SETELEM: + pn2->pn_op = JSOP_GETELEM; + break; + default: + PR_ASSERT(0); } -#endif - js_ReportCompileError(cx, ts, "missing ; before statement"); - return JS_FALSE; } - return JS_TRUE; + return pn; } static JSBool -MatchLabel(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, - JSAtom **labelp) +MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) { + JSAtom *label; #if JS_HAS_LABEL_STATEMENT JSTokenType tt; - tt = js_PeekTokenSameLine(cx, ts, cg); + tt = js_PeekTokenSameLine(cx, ts); if (tt == TOK_ERROR) return JS_FALSE; if (tt == TOK_NAME) { - if (js_GetToken(cx, ts, cg) == TOK_ERROR) - return JS_FALSE; - *labelp = ts->token.u.atom; - js_IndexAtom(cx, *labelp, &cg->atomList); + (void) js_GetToken(cx, ts); + label = ts->token.t_atom; } else { - *labelp = NULL; + label = NULL; } #else - *labelp = NULL; + label = NULL; #endif - return WellTerminated(cx, ts, cg, TOK_ERROR); + pn->pn_atom = label; + if (pn->pn_pos.end.lineno == ts->lineno) + return WellTerminated(cx, ts, TOK_ERROR); + return JS_TRUE; } #if JS_HAS_EXPORT_IMPORT -static JSBool -ImportExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - ptrdiff_t begin; - jsatomid atomIndex; - JSOp op; + JSParseNode *pn, *pn2, *pn3; JSTokenType tt; - begin = CG_OFFSET(cg); MUST_MATCH_TOKEN(TOK_NAME, "missing name in import statement"); - atomIndex = js_IndexAtom(cx, ts->token.u.atom, &cg->atomList); - op = JSOP_NAME; + pn = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn) + return NULL; + pn->pn_op = JSOP_NAME; + pn->pn_atom = ts->token.t_atom; + pn->pn_slot = -1; - ts->flags |= TSF_LOOKAHEAD; - while ((tt = js_GetToken(cx, ts, cg)) == TOK_DOT || tt == TOK_LB) { - ts->flags &= ~TSF_LOOKAHEAD; - switch (op) { - case JSOP_NAME: - case JSOP_GETPROP: - EMIT_CONST_ATOM_OP(op, atomIndex); - break; - case JSOP_GETELEM: - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; - case JSOP_IMPORTALL: + ts->flags |= TSF_REGEXP; + while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) { + ts->flags &= ~TSF_REGEXP; + if (pn->pn_op == JSOP_IMPORTALL) goto bad_import; - default:; - } - if (!js_FlushNewlines(cx, ts, cg)) - return JS_FALSE; if (tt == TOK_DOT) { - /* - * Compile import-property opcode. - */ - if (js_MatchToken(cx, ts, cg, TOK_STAR)) { - op = JSOP_IMPORTALL; + pn2 = NewParseNode(cx, &ts->token, PN_NAME); + if (!pn2) + return NULL; + pn2->pn_expr = pn; + if (js_MatchToken(cx, ts, TOK_STAR)) { + pn2->pn_op = JSOP_IMPORTALL; + pn2->pn_atom = NULL; } else { MUST_MATCH_TOKEN(TOK_NAME, "missing name after . operator"); - - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - (ptrdiff_t)(CG_OFFSET(cg) - begin)) < 0) { - return JS_FALSE; - } - atomIndex = js_IndexAtom(cx, ts->token.u.atom, &cg->atomList); - op = JSOP_GETPROP; + pn2->pn_op = JSOP_GETPROP; + pn2->pn_atom = ts->token.t_atom; + pn2->pn_slot = -1; } + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = ts->token.pos.end; } else { - /* - * Compile index expression and get-slot opcode. - */ - if (!Expr(cx, ts, cg)) - return JS_FALSE; + /* Make a TOK_LB node. */ + pn2 = NewParseNode(cx, &ts->token, PN_BINARY); + if (!pn2) + return NULL; + pn3 = Expr(cx, ts, tc); + if (!pn3) + return NULL; + /* [balance: */ MUST_MATCH_TOKEN(TOK_RB, "missing ] in index expression"); + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = ts->token.pos.end; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - (ptrdiff_t)(CG_OFFSET(cg) - begin)) < 0) { - return JS_FALSE; + /* Optimize o['p'] to o.p by rewriting pn2. */ + if (pn3->pn_type == TOK_STRING) { + pn2->pn_type = TOK_DOT; + pn2->pn_op = JSOP_GETPROP; + pn2->pn_arity = PN_NAME; + pn2->pn_expr = pn; + pn2->pn_atom = pn3->pn_atom; + } else { + pn2->pn_op = JSOP_GETELEM; + pn2->pn_left = pn; + pn2->pn_right = pn3; } - op = JSOP_GETELEM; } - ts->flags |= TSF_LOOKAHEAD; + pn = pn2; + ts->flags |= TSF_REGEXP; } - ts->flags &= ~TSF_LOOKAHEAD; + ts->flags &= ~TSF_REGEXP; if (tt == TOK_ERROR) - return JS_FALSE; + return NULL; js_UngetToken(ts); - switch (op) { + switch (pn->pn_op) { case JSOP_GETPROP: - EMIT_CONST_ATOM_OP(JSOP_IMPORTPROP, atomIndex); + pn->pn_op = JSOP_IMPORTPROP; break; case JSOP_GETELEM: - op = JSOP_IMPORTELEM; - /* FALL THROUGH */ + pn->pn_op = JSOP_IMPORTELEM; + break; case JSOP_IMPORTALL: - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; break; default: goto bad_import; } - return JS_TRUE; + return pn; bad_import: js_ReportCompileError(cx, ts, "invalid import expression"); - return JS_FALSE; + return NULL; } #endif /* JS_HAS_EXPORT_IMPORT */ -static JSBool -Statement(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - StmtInfo stmtInfo, *stmt, *stmt2; - intN snindex; - uintN lineno, topNoteCount; - JSBool ok, isForInLoop, hasVarNote; - ptrdiff_t top, beq, jmp; - JSCodeGenerator cg2; - jsbytecode *pc; - JSOp lastop; - jsatomid atomIndex; JSTokenType tt, lastExprType; + JSParseNode *pn, *pn1, *pn2, *pn3, *pn4; + JSStmtInfo stmtInfo, *stmt, *stmt2; JSAtom *label; - switch (js_GetToken(cx, ts, cg)) { + ts->flags |= TSF_REGEXP; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_REGEXP; + + switch (tt) { #if JS_HAS_EXPORT_IMPORT case TOK_EXPORT: - lineno = ts->lineno; - if (js_MatchToken(cx, ts, cg, TOK_STAR)) { - if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0) - return JS_FALSE; + pn = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn) + return NULL; + PN_INIT_LIST(pn); + if (js_MatchToken(cx, ts, TOK_STAR)) { + pn2 = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); } else { do { MUST_MATCH_TOKEN(TOK_NAME, "missing name in export statement"); - atomIndex = js_IndexAtom(cx, ts->token.u.atom, &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_EXPORTNAME, atomIndex); - } while (js_MatchToken(cx, ts, cg, TOK_COMMA)); + pn2 = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn2) + return NULL; + pn2->pn_op = JSOP_NAME; + pn2->pn_atom = ts->token.t_atom; + pn2->pn_slot = -1; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); + } + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; + if (pn->pn_pos.end.lineno == ts->lineno && + !WellTerminated(cx, ts, TOK_ERROR)) { + return NULL; } - if (ts->lineno == lineno && !WellTerminated(cx, ts, cg, TOK_ERROR)) - return JS_FALSE; break; case TOK_IMPORT: - lineno = ts->lineno; + pn = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn) + return NULL; + PN_INIT_LIST(pn); do { - if (!ImportExpr(cx, ts, cg)) - return JS_FALSE; - } while (js_MatchToken(cx, ts, cg, TOK_COMMA)); - if (ts->lineno == lineno && !WellTerminated(cx, ts, cg, TOK_ERROR)) - return JS_FALSE; + pn2 = ImportExpr(cx, ts, tc); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; + if (pn->pn_pos.end.lineno == ts->lineno && + !WellTerminated(cx, ts, TOK_ERROR)) { + return NULL; + } break; #endif /* JS_HAS_EXPORT_IMPORT */ case TOK_IF: - if (!Condition(cx, ts, cg)) - return JS_FALSE; - js_PushStatement(cg, &stmtInfo, STMT_IF, CG_OFFSET(cg)); - snindex = js_NewSrcNote(cx, cg, SRC_IF); - if (snindex < 0) - return JS_FALSE; - beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); - if (beq < 0) - return JS_FALSE; - if (!Statement(cx, ts, cg)) - return JS_FALSE; - if (js_MatchToken(cx, ts, cg, TOK_ELSE)) { + /* An IF node has three kids: condition, then, and optional else. */ + pn = NewParseNode(cx, &ts->token, PN_TERNARY); + if (!pn) + return NULL; + pn1 = Condition(cx, ts, tc); + if (!pn1) + return NULL; + js_PushStatement(tc, &stmtInfo, STMT_IF, -1); + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + if (js_MatchToken(cx, ts, TOK_ELSE)) { stmtInfo.type = STMT_ELSE; - SN_SET_TYPE(&cg->notes[snindex], SRC_IF_ELSE); - jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); - if (jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (!Statement(cx, ts, cg)) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + pn3 = Statement(cx, ts, tc); + if (!pn3) + return NULL; + pn->pn_pos.end = pn3->pn_pos.end; } else { - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + pn3 = NULL; + pn->pn_pos.end = pn2->pn_pos.end; } - return js_PopStatement(cx, cg); + js_PopStatement(tc); + pn->pn_kid1 = pn1; + pn->pn_kid2 = pn2; + pn->pn_kid3 = pn3; + return pn; #if JS_HAS_SWITCH_STATEMENT case TOK_SWITCH: { - JSOp switchop; uintN newlines; - SwitchCase *caselist; /* singly-linked case list */ - SwitchCase **casetail; /* pointer to pointer to last case */ - SwitchCase *defaultsc; /* default switch case or null */ - uint32 ncases, tablen; - JSAtomMap map; - JSScript *script; - jsval value; - SwitchCase *sc; - jsint i, low, high; - size_t length; - ptrdiff_t offset; + JSParseNode *pn5, *pn6; + pn = NewParseNode(cx, &ts->token, PN_TERNARY); + if (!pn) + return NULL; MUST_MATCH_TOKEN(TOK_LP, "missing ( before switch expression"); - if (!Expr(cx, ts, cg)) - return JS_FALSE; + + /* pn1 points to the switch's discriminant. */ + pn1 = Expr(cx, ts, tc); + if (!pn1) + return NULL; + MUST_MATCH_TOKEN(TOK_RP, "missing ) after switch expression"); MUST_MATCH_TOKEN(TOK_LC, "missing { before switch body"); /* balance} */ - top = CG_OFFSET(cg); - js_PushStatement(cg, &stmtInfo, STMT_SWITCH, top); + /* pn2 is a list of case nodes, not including the default case. */ + pn2 = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn2) + return NULL; + PN_INIT_LIST(pn2); - snindex = js_NewSrcNote2(cx, cg, SRC_SWITCH, 0); - if (snindex < 0) - return JS_FALSE; - topNoteCount = cg->noteCount; - - switchop = JSOP_TABLESWITCH; - if (js_Emit1(cx, cg, switchop) < 0) - return JS_FALSE; - - ok = JS_TRUE; - cg2.base = NULL; - caselist = NULL; - casetail = &caselist; - defaultsc = NULL; - ncases = 0; + /* If not null, pn3 points to the default case's node. */ + pn3 = NULL; + js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1); newlines = ts->flags & TSF_NEWLINES; if (newlines) HIDE_NEWLINES(ts); - while ((tt = js_GetToken(cx, ts, cg)) != TOK_RC) { + while ((tt = js_GetToken(cx, ts)) != TOK_RC) { switch (tt) { case TOK_CASE: - if (!cg2.base) { - ok = js_InitCodeGenerator(cx, &cg2, &cx->tempPool); - if (!ok) - goto end_switch; - } else { - CG_RESET(&cg2); - } - lineno = ts->lineno; - - /* Save atoms indexed but not mapped before compiling. */ - ok = js_InitAtomMap(cx, &map, &cg->atomList); - if (!ok) - goto end_switch; - ok = Expr(cx, ts, &cg2) && js_Emit1(cx, &cg2, JSOP_POPV) >= 0; - if (ok) { - script = js_NewScript(cx, &cg2, ts->filename, lineno, - ts->principals, NULL); - if (!script) - ok = JS_FALSE; - } - for (i = 0; i < (jsint)map.length; i++) { - js_IndexAtom(cx, map.vector[i], &cg->atomList); - PR_ASSERT((jsint)map.vector[i]->index == i); - } - js_FreeAtomMap(cx, &map); - - if (!ok) - goto end_switch; - ok = js_Execute(cx, cx->fp->scopeChain, script, cx->fp, &value); - js_DestroyScript(cx, script); - if (!ok) - goto end_switch; - - if (!JSVAL_IS_NUMBER(value) && - !JSVAL_IS_STRING(value) && - !JSVAL_IS_BOOLEAN(value)) { - js_ReportCompileError(cx, ts, "invalid case expression"); - ok = JS_FALSE; - goto end_switch; - } - if (++ncases == PR_BIT(16)) { + pn4 = NewParseNode(cx, &ts->token, PN_BINARY); + if (!pn4) + goto bad_switch; + pn4->pn_left = Expr(cx, ts, tc); + if (!pn4->pn_left) + goto bad_switch; + PN_APPEND(pn2, pn4); + if (pn2->pn_count == PR_BIT(16)) { js_ReportCompileError(cx, ts, "too many switch cases"); - ok = JS_FALSE; - goto end_switch; + goto bad_switch; } - JSVAL_LOCK(cx, value); break; case TOK_DEFAULT: - if (defaultsc) { + if (pn3) { js_ReportCompileError(cx, ts, "more than one switch default"); - ok = JS_FALSE; - goto end_switch; + goto bad_switch; } - value = JSVAL_VOID; + pn4 = NULL; break; case TOK_ERROR: - ok = JS_FALSE; - goto end_switch; + goto bad_switch; default: js_ReportCompileError(cx, ts, "invalid switch statement"); - ok = JS_FALSE; - goto end_switch; + goto bad_switch; } - MUST_MATCH_TOKEN_THROW(TOK_COLON, "missing : after case expression", - ok = JS_FALSE; goto end_switch); + MUST_MATCH_TOKEN(TOK_COLON, "missing : after case label"); - PR_ARENA_ALLOCATE(sc, &cx->tempPool, sizeof(SwitchCase)); - if (!sc) { - JSVAL_UNLOCK(cx, value); - JS_ReportOutOfMemory(cx); - ok = JS_FALSE; - goto end_switch; - } - sc->value = value; - sc->offset = CG_OFFSET(cg); - sc->next = NULL; - *casetail = sc; - casetail = &sc->next; - - if (tt == TOK_DEFAULT) - defaultsc = sc; - - while ((tt = js_PeekToken(cx, ts, cg)) != TOK_RC && + pn5 = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn5) + goto bad_switch; + pn5->pn_type = TOK_LC; + PN_INIT_LIST(pn5); + while ((tt = js_PeekToken(cx, ts)) != TOK_RC && tt != TOK_CASE && tt != TOK_DEFAULT) { - if (tt == TOK_ERROR) { - ok = JS_FALSE; - goto end_switch; - } - ok = Statement(cx, ts, cg); - if (!ok) - goto end_switch; + if (tt == TOK_ERROR) + goto bad_switch; + pn6 = Statement(cx, ts, tc); + if (!pn6) + goto bad_switch; + pn5->pn_pos.end = pn6->pn_pos.end; + PN_APPEND(pn5, pn6); + } + if (pn4) { + pn4->pn_pos.end = pn5->pn_pos.end; + pn4->pn_right = pn5; + } else { + pn3 = pn5; } } - if (!caselist) { - low = high = 0; - tablen = 0; - } else { - low = JSVAL_INT_MAX; - high = JSVAL_INT_MIN; - for (sc = caselist; sc; sc = sc->next) { - if (sc == defaultsc) - continue; - if (!JSVAL_IS_INT(sc->value)) { - switchop = JSOP_LOOKUPSWITCH; - break; - } - i = JSVAL_TO_INT(sc->value); - if ((jsuint)(i + (jsint)PR_BIT(15)) >= (jsuint)PR_BIT(16)) { - switchop = JSOP_LOOKUPSWITCH; - break; - } - if (i < low) - low = i; - if (high < i) - high = i; - } - tablen = (uint32)(high - low + 1); - if (tablen >= PR_BIT(16) || tablen > 2 * ncases) - switchop = JSOP_LOOKUPSWITCH; - } - - if (switchop == JSOP_TABLESWITCH) { - length = (size_t)(6 + 2 * tablen); - } else { - length = (size_t)(4 + 4 * ncases); - *CG_CODE(cg, top) = switchop; - } - - /* Slide body code down to make room for the jump or lookup table. */ - if (js_MoveCode(cx, cg, top + 1, cg, top + 1 + length) < 0) { - ok = JS_FALSE; - goto end_switch; - } - pc = CG_CODE(cg, top); - - /* Don't forget to adjust the case offsets. */ - for (sc = caselist; sc; sc = sc->next) - sc->offset += length; - - /* If the body has source notes, bump the first one's delta. */ - if (cg->noteCount > topNoteCount) { - ok = js_BumpSrcNoteDelta(cx, cg, topNoteCount, (ptrdiff_t)length); - if (!ok) - goto end_switch; - } - - /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ - offset = CG_OFFSET(cg) - top; - ok = js_SetSrcNoteOffset(cx, cg, snindex, 0, (ptrdiff_t)offset); - if (!ok) - goto end_switch; - - /* Fill in the default jump offset. */ - if (defaultsc) - offset = defaultsc->offset - top; - else - offset = CG_OFFSET(cg) - top; - ok = js_SetJumpOffset(cx, cg, pc, offset); - if (!ok) - goto end_switch; - pc += 2; - - /* Fill in jump or lookup table. */ - if (switchop == JSOP_TABLESWITCH) { - SET_JUMP_OFFSET(pc, low); - pc += 2; - SET_JUMP_OFFSET(pc, high); - pc += 2; - if (tablen) { - size_t tablesize; - SwitchCase **table; - - tablesize = (size_t)tablen * sizeof *table; - PR_ARENA_ALLOCATE(table, &cx->tempPool, tablesize); - if (!table) { - JS_ReportOutOfMemory(cx); - ok = JS_FALSE; - goto end_switch; - } - memset(table, 0, tablesize); - for (sc = caselist; sc; sc = sc->next) { - if (sc == defaultsc) - continue; - i = JSVAL_TO_INT(sc->value); - i -= low; - PR_ASSERT((uint32)i < tablen); - table[i] = sc; - } - for (i = 0; i < (jsint)tablen; i++) { - sc = table[i]; - offset = sc ? sc->offset - top : 0; - ok = js_SetJumpOffset(cx, cg, pc, offset); - if (!ok) - goto end_switch; - pc += 2; - } - } - } else { - SET_ATOM_INDEX(pc, ncases); - pc += 2; - - for (sc = caselist; sc; sc = sc->next) { - JSAtom *atom; - - if (sc == defaultsc) - continue; - value = sc->value; - atom = js_AtomizeValue(cx, value, 0); - if (!atom) { - ok = JS_FALSE; - goto end_switch; - } - atomIndex = js_IndexAtom(cx, atom, &cg->atomList); - js_DropAtom(cx, atom); - - SET_ATOM_INDEX(pc, atomIndex); - pc += 2; - - offset = sc->offset - top; - ok = js_SetJumpOffset(cx, cg, pc, offset); - if (!ok) - goto end_switch; - pc += 2; - } - } - - end_switch: - for (sc = caselist; sc; sc = sc->next) - JSVAL_UNLOCK(cx, sc->value); if (newlines) SCAN_NEWLINES(ts); - return js_PopStatement(cx, cg); + js_PopStatement(tc); + + pn->pn_pos.end = pn2->pn_pos.end = ts->token.pos.end; + pn->pn_kid1 = pn1; + pn->pn_kid2 = pn2; + pn->pn_kid3 = pn3; + return pn; + + bad_switch: + if (newlines) + SCAN_NEWLINES(ts); + return NULL; } #endif /* JS_HAS_SWITCH_STATEMENT */ case TOK_WHILE: - top = CG_OFFSET(cg); - js_PushStatement(cg, &stmtInfo, STMT_WHILE_LOOP, top); - if (!Condition(cx, ts, cg)) - return JS_FALSE; - snindex = js_NewSrcNote(cx, cg, SRC_WHILE); - if (snindex < 0) - return JS_FALSE; - beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); - if (beq < 0) - return JS_FALSE; - if (!Statement(cx, ts, cg)) - return JS_FALSE; - jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); - if (jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,jmp), top - jmp); - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - return js_PopStatement(cx, cg); + pn = NewParseNode(cx, &ts->token, PN_BINARY); + if (!pn) + return NULL; + js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); + pn2 = Condition(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_left = pn2; + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + js_PopStatement(tc); + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_right = pn2; + return pn; #if JS_HAS_DO_WHILE_LOOP case TOK_DO: - /* Emit an annotated nop so we know to decompile a 'do' keyword. */ - if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* Compile the loop body and condition. */ - top = CG_OFFSET(cg); - js_PushStatement(cg, &stmtInfo, STMT_DO_LOOP, top); - if (!Statement(cx, ts, cg)) - return JS_FALSE; + pn = NewParseNode(cx, &ts->token, PN_BINARY); + if (!pn) + return NULL; + js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_left = pn2; MUST_MATCH_TOKEN(TOK_WHILE, "missing while after do-loop body"); - if (!Condition(cx, ts, cg)) - return JS_FALSE; - - /* Re-use the SRC_WHILE note, this time for the JSOP_IFNE opcode. */ - if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0) - return JS_FALSE; - jmp = top - CG_OFFSET(cg); - if (js_Emit3(cx, cg, JSOP_IFNE, - JUMP_OFFSET_HI(jmp), JUMP_OFFSET_LO(jmp)) < 0) { - return JS_FALSE; - } - return js_PopStatement(cx, cg); + pn2 = Condition(cx, ts, tc); + if (!pn2) + return NULL; + js_PopStatement(tc); + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_right = pn2; + break; #endif /* JS_HAS_DO_WHILE_LOOP */ case TOK_FOR: - top = CG_OFFSET(cg); - js_PushStatement(cg, &stmtInfo, STMT_FOR_LOOP, top); - snindex = -1; - topNoteCount = cg->noteCount; - cg2.base = NULL; + /* A FOR node is binary, left is loop control and right is the body. */ + pn = NewParseNode(cx, &ts->token, PN_BINARY); + if (!pn) + return NULL; + js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); MUST_MATCH_TOKEN(TOK_LP, "missing ( after for"); /* balance) */ - tt = js_PeekToken(cx, ts, cg); + tt = js_PeekToken(cx, ts); if (tt == TOK_SEMI) { - /* No initializer -- emit an annotated nop for the decompiler. */ - snindex = js_NewSrcNote(cx, cg, SRC_FOR); - if (snindex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) - return JS_FALSE; + /* No initializer -- set first kid of left sub-node to null. */ + pn1 = NULL; } else { + /* Set pn1 to a var list or an initializing expression. */ if (tt == TOK_VAR) { - (void) js_GetToken(cx, ts, cg); - if (!Variables(cx, ts, cg)) - return JS_FALSE; + (void) js_GetToken(cx, ts); + pn1 = Variables(cx, ts, tc); } else { - if (!Expr(cx, ts, cg)) - return JS_FALSE; - } - if (js_PeekToken(cx, ts, cg) != TOK_IN) { - snindex = js_NewSrcNote(cx, cg, SRC_FOR); - if (snindex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; + pn1 = Expr(cx, ts, tc); } + if (!pn1) + return NULL; } - isForInLoop = js_MatchToken(cx, ts, cg, TOK_IN); - if (isForInLoop) { - /* Insert a JSOP_PUSH to allocate an iterator before this loop. */ + /* + * 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))) { stmtInfo.type = STMT_FOR_IN_LOOP; - if (js_MoveCode(cx, cg, top, cg, top + 1) < 0) - return JS_FALSE; - *CG_CODE(cg, top) = JSOP_PUSH; - js_UpdateDepth(cx, cg, top); - top++; - SET_STATEMENT_TOP(&stmtInfo, top); - /* If the iterator has a SRC_VAR note, remember to emit it later. */ - hasVarNote = (cg->noteCount > topNoteCount); - if (hasVarNote) { - hasVarNote = (SN_TYPE(&cg->notes[topNoteCount]) == SRC_VAR); - if (hasVarNote) { - /* XXX blow away any ASSIGNOP notes in initializers */ - cg->noteCount = topNoteCount; - } else { - /* Otherwise bump the first notes's delta. */ - if (!js_BumpSrcNoteDelta(cx, cg, topNoteCount, 1)) - return JS_FALSE; + 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; } - } - - /* Cancel the last bytecode, saving it for re-emission later. */ - pc = CG_CODE(cg, cg->lastCodeOffset); - lastop = (JSOp) *pc; - switch (lastop) { - case JSOP_NAME: - case JSOP_GETPROP: - atomIndex = GET_ATOM_INDEX(pc); - break; - case JSOP_GETELEM: - /* GETELEM and FORELEM have no immediate operand. */ - break; + 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 && + pn1->pn_type != TOK_DOT && + pn1->pn_type != TOK_LB) { js_ReportCompileError(cx, ts, "invalid for/in left-hand side"); - return JS_FALSE; - } - js_CancelLastOpcode(cx, cg, &ts->newlines); - - /* Now compile the object expression over which we're iterating. */ - if (!Expr(cx, ts, cg)) - return JS_FALSE; - - if (lastop == JSOP_NAME) { - if (hasVarNote && js_NewSrcNote(cx, cg, SRC_VAR) < 0) - return JS_FALSE; - } else { - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - (int16)(CG_OFFSET(cg) - top)) < 0) { - return JS_FALSE; - } + return NULL; } - /* Re-emit the left-hand side's bytecode as a JSOP_FOR* variant. */ - switch (lastop) { - case JSOP_NAME: - EMIT_CONST_ATOM_OP(JSOP_FORNAME, atomIndex); - break; - case JSOP_GETPROP: - EMIT_CONST_ATOM_OP(JSOP_FORPROP, atomIndex); - break; - case JSOP_GETELEM: - if (js_Emit1(cx, cg, JSOP_FORELEM) < 0) - return JS_FALSE; - break; - default:; + /* 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; } - - /* Pop and test the loop condition generated by JSOP_FOR*. */ - beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); - if (beq < 0) - return JS_FALSE; + pn->pn_left = pn2; } else { + /* Parse the loop condition or null into pn2. */ MUST_MATCH_TOKEN(TOK_SEMI, "missing ; after for-loop initializer"); - top = CG_OFFSET(cg); - SET_STATEMENT_TOP(&stmtInfo, top); - if (js_PeekToken(cx, ts, cg) == TOK_SEMI) { - /* No loop condition -- flag this fact in the source note. */ - if (!js_SetSrcNoteOffset(cx, cg, snindex, 0, 0)) - return JS_FALSE; - beq = 0; + if (js_PeekToken(cx, ts) == TOK_SEMI) { + pn2 = NULL; } else { - if (!Expr(cx, ts, cg)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, snindex, 0, - (ptrdiff_t)(CG_OFFSET(cg) - top))) { - return JS_FALSE; - } - beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); - if (beq < 0) - return JS_FALSE; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; } + /* Parse the update expression or null into pn3. */ MUST_MATCH_TOKEN(TOK_SEMI, "missing ; after for-loop condition"); - if (js_PeekToken(cx, ts, cg) != TOK_RP) { - if (!js_InitCodeGenerator(cx, &cg2, &cx->tempPool)) - return JS_FALSE; - CG_PUSH(cg, &cg2); - if (js_NewSrcNote2(cx, &cg2, SRC_SETLINE, - (ptrdiff_t)TRUE_LINENO(ts)) < 0) { - return JS_FALSE; - } - if (!Expr(cx, ts, &cg2)) - return JS_FALSE; - if (js_Emit1(cx, &cg2, JSOP_POP) < 0) - return JS_FALSE; - CG_POP(cg, &cg2); + if (js_PeekToken(cx, ts) == TOK_RP) { + pn3 = NULL; + } else { + pn3 = Expr(cx, ts, tc); + if (!pn3) + return NULL; } + + /* Build the RESERVED node to use as the left kid of pn. */ + pn4 = NewParseNode(cx, &ts->token, PN_TERNARY); + if (!pn4) + return NULL; + pn4->pn_type = TOK_RESERVED; + pn4->pn_kid1 = pn1; + pn4->pn_kid2 = pn2; + pn4->pn_kid3 = pn3; + pn->pn_left = pn4; } /* (balance: */ MUST_MATCH_TOKEN(TOK_RP, "missing ) after for-loop control"); - if (!Statement(cx, ts, cg)) - return JS_FALSE; - if (snindex != -1 && - !js_SetSrcNoteOffset(cx, cg, snindex, 1, - (ptrdiff_t)(CG_OFFSET(cg) - top))) { - return JS_FALSE; - } + /* Parse the loop body into pn->pn_right. */ + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_right = pn2; + js_PopStatement(tc); - if (cg2.base) { - /* Set loop and enclosing "update" offsets, for continue. */ - stmt = &stmtInfo; - stmt->update = CG_OFFSET(cg); - while ((stmt = stmt->down) != 0 && stmt->type == STMT_LABEL) - stmt->update = CG_OFFSET(cg); + /* Record the absolute line number for source note emission. */ + pn->pn_pos.end = pn2->pn_pos.end; + return pn; - /* Copy the update code from its temporary pool into cg's pool. */ - if (!js_MoveSrcNotes(cx, &cg2, cg)) - return JS_FALSE; - if (js_MoveCode(cx, &cg2, 0, cg, stmtInfo.update) < 0) - return JS_FALSE; - - /* Restore the absolute line number for source note readers. */ - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)TRUE_LINENO(ts)) - < 0) { - return JS_FALSE; +#if JS_HAS_EXCEPTIONS + case TOK_TRY: + /* + * try nodes are ternary. + * kid1 is the try Statement + * kid2 is the catch node + * kid3 is the finally Statement + */ + pn = NewParseNode(cx, &ts->token, PN_TERNARY); + pn1 = Statement(cx, ts, tc); + if (!pn1) + return NULL; + if (js_PeekToken(cx, ts) == TOK_CATCH) { + pn2 = NewParseNode(cx, &ts->token, PN_BINARY); + if (!pn2) + 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; + } } - } - if (snindex != -1 && - !js_SetSrcNoteOffset(cx, cg, snindex, 2, - (ptrdiff_t)(CG_OFFSET(cg) - top))) { - return JS_FALSE; + /* 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); + } + 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) + return NULL; + } else { + pn2 = NULL; } + if (js_MatchToken(cx, ts, TOK_FINALLY)) { + pn3 = Statement(cx, ts, tc); + if (!pn3) + return NULL; + } else { + pn3 = NULL; + } + if (!pn2 && !pn3) { + 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; - jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); - if (jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,jmp), top - jmp); - if (beq > 0) - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (!js_PopStatement(cx, cg)) - return JS_FALSE; - if (isForInLoop) { - /* - * Generate the iterator pop after popping the stmtInfo stack, so - * breaks will go to the pop bytecode. - */ - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; + case TOK_THROW: + pn = NewParseNode(cx, &ts->token, PN_UNARY); + if (!pn) + return NULL; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; + if (pn->pn_pos.end.lineno == ts->lineno && + !WellTerminated(cx, ts, TOK_ERROR)) { + return NULL; } - return JS_TRUE; + pn->pn_op = JSOP_THROW; + pn->pn_kid = pn2; + break; +#endif /* JS_HAS_EXCEPTIONS */ case TOK_BREAK: - if (!MatchLabel(cx, ts, cg, &label)) - return JS_FALSE; - stmt = cg->stmtInfo; + pn = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn) + return NULL; + if (!MatchLabel(cx, ts, pn)) + return NULL; + stmt = tc->topStmt; + label = pn->pn_atom; if (label) { for (; ; stmt = stmt->down) { if (!stmt) { js_ReportCompileError(cx, ts, "label not found"); - return JS_FALSE; + return NULL; } if (stmt->type == STMT_LABEL && stmt->label == label) break; @@ -1235,31 +1229,35 @@ Statement(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) for (; ; stmt = stmt->down) { if (!stmt) { js_ReportCompileError(cx, ts, "invalid break"); - return JS_FALSE; + return NULL; } if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH) break; } } - if (js_EmitBreak(cx, cg, stmt, label) < 0) - return JS_FALSE; + if (label) + pn->pn_pos.end = ts->token.pos.end; break; case TOK_CONTINUE: - if (!MatchLabel(cx, ts, cg, &label)) - return JS_FALSE; - stmt = cg->stmtInfo; + pn = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn) + return NULL; + if (!MatchLabel(cx, ts, pn)) + return NULL; + stmt = tc->topStmt; + label = pn->pn_atom; if (label) { for (stmt2 = NULL; ; stmt = stmt->down) { if (!stmt) { js_ReportCompileError(cx, ts, "label not found"); - return JS_FALSE; + return NULL; } if (stmt->type == STMT_LABEL) { if (stmt->label == label) { if (!stmt2 || !STMT_IS_LOOP(stmt2)) { js_ReportCompileError(cx, ts, "invalid continue"); - return JS_FALSE; + return NULL; } break; } @@ -1271,185 +1269,193 @@ Statement(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) for (; ; stmt = stmt->down) { if (!stmt) { js_ReportCompileError(cx, ts, "invalid continue"); - return JS_FALSE; + return NULL; } if (STMT_IS_LOOP(stmt)) break; } - if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) - return JS_FALSE; } - if (js_EmitContinue(cx, cg, stmt, label) < 0) - return JS_FALSE; + if (label) + pn->pn_pos.end = ts->token.pos.end; break; case TOK_WITH: + pn = NewParseNode(cx, &ts->token, PN_BINARY); + if (!pn) + return NULL; MUST_MATCH_TOKEN(TOK_LP, "missing ( before with-statement object"); /* balance) */ - if (!Expr(cx, ts, cg)) - return JS_FALSE; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; /* (balance: */ MUST_MATCH_TOKEN(TOK_RP, "missing ) after with-statement object"); + pn->pn_left = pn2; - js_PushStatement(cg, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); - if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) - return JS_FALSE; - if (!Statement(cx, ts, cg)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) - return JS_FALSE; - return js_PopStatement(cx, cg); + js_PushStatement(tc, &stmtInfo, STMT_WITH, -1); + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + js_PopStatement(tc); + + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_right = pn2; + return pn; case TOK_VAR: - lineno = ts->lineno; - if (!Variables(cx, ts, cg)) - return JS_FALSE; - if (ts->lineno == lineno && !WellTerminated(cx, ts, cg, TOK_ERROR)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; + pn = Variables(cx, ts, tc); + if (!pn) + return NULL; + if (pn->pn_pos.end.lineno == ts->lineno && + !WellTerminated(cx, ts, TOK_ERROR)) { + return NULL; + } + /* Tell js_EmitTree to generate a final POP. */ + pn->pn_op = JSOP_POP; break; case TOK_RETURN: - if (!(ts->flags & TSF_FUNCTION)) { + if (!(tc->flags & TCF_IN_FUNCTION)) { js_ReportCompileError(cx, ts, "invalid return"); - return JS_FALSE; + return NULL; } + pn = NewParseNode(cx, &ts->token, PN_UNARY); + if (!pn) + return NULL; /* This is ugly, but we don't want to require a semicolon. */ ts->flags |= TSF_REGEXP; - tt = js_PeekTokenSameLine(cx, ts, cg); + tt = js_PeekTokenSameLine(cx, ts); ts->flags &= ~TSF_REGEXP; if (tt == TOK_ERROR) - return JS_FALSE; + return NULL; if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { - lineno = ts->lineno; - if (!Expr(cx, ts, cg)) - return JS_FALSE; - if (ts->lineno == lineno && !WellTerminated(cx, ts, cg, TOK_ERROR)) - return JS_FALSE; - ts->flags |= TSF_RETURN_EXPR; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + if (pn2->pn_pos.end.lineno == ts->lineno && + !WellTerminated(cx, ts, TOK_ERROR)) { + return NULL; + } + tc->flags |= TCF_RETURN_EXPR; + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_kid = pn2; } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - ts->flags |= TSF_RETURN_VOID; + tc->flags |= TCF_RETURN_VOID; + pn->pn_kid = NULL; } - if ((ts->flags & (TSF_RETURN_EXPR | TSF_RETURN_VOID)) == - (TSF_RETURN_EXPR | TSF_RETURN_VOID)) { - js_ReportCompileError(cx, ts, - "function does not always return a value"); - return JS_FALSE; + if ((tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == + (TCF_RETURN_EXPR | TCF_RETURN_VOID)) { + js_ReportCompileError(cx, ts, badreturn_str); + return NULL; } - - if (js_Emit1(cx, cg, JSOP_RETURN) < 0) - return JS_FALSE; break; case TOK_LC: - js_PushStatement(cg, &stmtInfo, STMT_BLOCK, CG_OFFSET(cg)); - if (!Statements(cx, ts, cg)) - return JS_FALSE; + js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); + pn = Statements(cx, ts, tc); + if (!pn) + return NULL; /* {balance: */ MUST_MATCH_TOKEN(TOK_RC, "missing } in compound statement"); - return js_PopStatement(cx, cg); + js_PopStatement(tc); + return pn; case TOK_EOL: case TOK_SEMI: - return JS_TRUE; + pn = NewParseNode(cx, &ts->token, PN_UNARY); + if (!pn) + return NULL; + pn->pn_type = TOK_SEMI; + pn->pn_kid = NULL; + return pn; case TOK_ERROR: - return JS_FALSE; + return NULL; default: lastExprType = ts->token.type; js_UngetToken(ts); - lineno = ts->lineno; - top = CG_OFFSET(cg); - if (!Expr(cx, ts, cg)) - return JS_FALSE; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; tt = ts->pushback.type; if (tt == TOK_COLON) { - if (cg->lastCodeOffset != top || *CG_CODE(cg, top) != JSOP_NAME) { + if (pn2->pn_type != TOK_NAME) { js_ReportCompileError(cx, ts, "invalid label"); - return JS_FALSE; + return NULL; } - label = cg->atomList.list; - for (stmt = cg->stmtInfo; stmt; stmt = stmt->down) { + label = pn2->pn_atom; + for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (stmt->type == STMT_LABEL && stmt->label == label) { js_ReportCompileError(cx, ts, "duplicate label"); - return JS_FALSE; + return NULL; } } - js_CancelLastOpcode(cx, cg, &ts->newlines); - js_GetToken(cx, ts, cg); - - /* Emit an annotated nop so we know to decompile a label. */ - snindex = js_NewSrcNote(cx, cg, - (js_PeekToken(cx, ts, cg) == TOK_LC) - ? SRC_LABELBRACE - : SRC_LABEL); - if (snindex < 0 || - !js_SetSrcNoteOffset(cx, cg, snindex, 0, - (ptrdiff_t)label->index) || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } + js_GetToken(cx, ts); /* Push a label struct and parse the statement. */ - js_PushStatement(cg, &stmtInfo, STMT_LABEL, CG_OFFSET(cg)); + js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1); stmtInfo.label = label; - if (!Statement(cx, ts, cg)) - return JS_FALSE; + pn = Statement(cx, ts, tc); + if (!pn) + return NULL; - /* If the statement was compound, emit a note for the end brace */ - if (SN_TYPE(&cg->notes[snindex]) == SRC_LABELBRACE) { - if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - } - - /* Finally, pop the label and return early. */ - return js_PopStatement(cx, cg); + /* Pop the label, set pn_expr, and return early. */ + js_PopStatement(tc); + pn2->pn_type = TOK_COLON; + pn2->pn_pos.end = pn->pn_pos.end; + pn2->pn_expr = pn; + return pn2; } - if (ts->lineno == lineno && !WellTerminated(cx, ts, cg, lastExprType)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POPV) < 0) - return JS_FALSE; + /* Check explicity against (multi-line) function statement */ + if (pn2->pn_pos.end.lineno == ts->lineno && + !WellTerminated(cx, ts, lastExprType)) { + return NULL; + } + pn = NewParseNode(cx, &ts->token, PN_UNARY); + if (!pn) + return NULL; + pn->pn_type = TOK_SEMI; + pn->pn_pos = pn2->pn_pos; + pn->pn_kid = pn2; break; } - (void) js_MatchToken(cx, ts, cg, TOK_SEMI); - return JS_TRUE; + (void) js_MatchToken(cx, ts, TOK_SEMI); + return pn; } -static JSBool -Variables(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { + JSParseNode *pn, *pn2; JSObject *obj, *pobj; JSFunction *fun; JSClass *clasp; - JSBool ok, first; JSPropertyOp getter, setter; - intN snindex; - ptrdiff_t offset, newoffset; JSAtom *atom; - jsatomid atomIndex; JSProperty *prop; - JSSymbol *var; - JSOp op; + JSScopeProperty *sprop; + JSBool ok; + + PR_ASSERT(ts->token.type == TOK_VAR); + pn = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn) + return NULL; + pn->pn_op = JSOP_NOP; + PN_INIT_LIST(pn); obj = js_FindVariableScope(cx, &fun); if (!obj) - return JS_FALSE; - - clasp = obj->map->clasp; - if (fun && clasp != &js_CallClass) { + return NULL; + clasp = OBJ_GET_CLASS(cx, obj); + if (fun && clasp == &js_FunctionClass) { getter = js_GetLocalVariable; setter = js_SetLocalVariable; } else { @@ -1457,982 +1463,871 @@ Variables(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) setter = clasp->setProperty; } - ok = first = JS_TRUE; - snindex = -1; - for (;;) { + do { MUST_MATCH_TOKEN(TOK_NAME, "missing variable name"); - atom = ts->token.u.atom; - atomIndex = js_IndexAtom(cx, atom, &cg->atomList); + atom = ts->token.t_atom; - pobj = NULL; - if (!js_LookupProperty(cx, obj, (jsval)atom, &pobj, &prop)) - return JS_FALSE; - if (prop && prop->object == obj) { - if (prop->getter == js_GetArgument) { + pn2 = NewParseNode(cx, &ts->token, PN_NAME); + if (!pn2) + return NULL; + pn2->pn_op = JSOP_NAME; + pn2->pn_atom = atom; + pn2->pn_expr = NULL; + pn2->pn_slot = -1; + PN_APPEND(pn, pn2); + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop)) + return NULL; + if (pobj == obj && + OBJ_IS_NATIVE(pobj) && + (sprop = (JSScopeProperty *)prop) != NULL) { + if (sprop->getter == js_GetArgument) { + getter = sprop->getter; #ifdef CHECK_ARGUMENT_HIDING js_ReportCompileError(cx, ts, "variable %s hides argument", ATOM_BYTES(atom)); ok = JS_FALSE; +#else + ok = JS_TRUE; #endif } else { + ok = JS_TRUE; if (fun) { /* Not an argument, must be a redeclared local var. */ - if (clasp != &js_CallClass) { - PR_ASSERT(prop->getter == js_GetLocalVariable); - PR_ASSERT(JSVAL_IS_INT(prop->id) && - JSVAL_TO_INT(prop->id) < fun->nvars); + if (clasp == &js_FunctionClass) { + PR_ASSERT(sprop->getter == js_GetLocalVariable); + PR_ASSERT(JSVAL_IS_INT(sprop->id) && + JSVAL_TO_INT(sprop->id) < fun->nvars); } } else { /* Global var: (re-)set id a la js_DefineProperty. */ - prop->id = ATOM_KEY(atom); + sprop->id = ATOM_KEY(atom); } - prop->getter = getter; - prop->setter = setter; - prop->flags |= JSPROP_ENUMERATE | JSPROP_PERMANENT; - prop->flags &= ~JSPROP_READONLY; + sprop->getter = getter; + sprop->setter = setter; + sprop->attrs |= JSPROP_ENUMERATE | JSPROP_PERMANENT; + sprop->attrs &= ~JSPROP_READONLY; } } else { - prop = js_DefineProperty(cx, obj, (jsval)atom, JSVAL_VOID, + sprop = NULL; + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + prop = NULL; + } + ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID, getter, setter, - JSPROP_ENUMERATE | JSPROP_PERMANENT); - if (!prop) - return JS_FALSE; - if (getter == js_GetLocalVariable) - prop->id = INT_TO_JSVAL(fun->nvars++); - var = prop->symbols; - var->next = cg->vars; - cg->vars = var; + JSPROP_ENUMERATE | JSPROP_PERMANENT, + &prop); + if (ok && prop) { + pobj = obj; + if (getter == js_GetLocalVariable) { + sprop = (JSScopeProperty *)prop; + sprop->id = INT_TO_JSVAL(fun->nvars++); + } + } } - if (js_MatchToken(cx, ts, cg, TOK_ASSIGN)) { - if (ts->token.u.op != JSOP_NOP) { + if (js_MatchToken(cx, ts, TOK_ASSIGN)) { + if (ts->token.t_op != JSOP_NOP) { js_ReportCompileError(cx, ts, "invalid variable initialization"); ok = JS_FALSE; + } else { + pn2->pn_expr = AssignExpr(cx, ts, tc); + if (pn2->pn_expr) + pn2->pn_op = JSOP_SETNAME2; + else + ok = JS_FALSE; } - if (!AssignExpr(cx, ts, cg)) - return JS_FALSE; - op = JSOP_SETNAME; - } else { - op = JSOP_NAME; } - if (first && js_NewSrcNote(cx, cg, SRC_VAR) < 0) - return JS_FALSE; - EMIT_CONST_ATOM_OP(op, atomIndex); - if (snindex >= 0) { - newoffset = CG_OFFSET(cg); - if (!js_SetSrcNoteOffset(cx, cg, snindex, 0, newoffset - offset)) - return JS_FALSE; - offset = newoffset; + + if (ok && fun && clasp == &js_FunctionClass && !InWithStatement(tc)) { + PR_ASSERT(sprop); + 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) { + 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 (!js_MatchToken(cx, ts, cg, TOK_COMMA)) - break; - offset = CG_OFFSET(cg); - snindex = js_NewSrcNote2(cx, cg, SRC_COMMA, 0); - if (snindex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - first = JS_FALSE; - } - return ok; + + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + if (!ok) + return NULL; + } while (js_MatchToken(cx, ts, TOK_COMMA)); + + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; + return pn; } -static JSBool -Expr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - intN snindex; - ptrdiff_t offset, newoffset; + JSParseNode *pn, *pn2; - snindex = -1; - for (;;) { - if (!AssignExpr(cx, ts, cg)) - return JS_FALSE; - if (snindex >= 0) { - newoffset = CG_OFFSET(cg); - if (!js_SetSrcNoteOffset(cx, cg, snindex, 0, newoffset - offset)) - return JS_FALSE; - offset = newoffset; + pn = AssignExpr(cx, ts, tc); + if (pn && js_MatchToken(cx, ts, TOK_COMMA)) { + pn2 = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn2) + return NULL; + pn2->pn_pos.begin = pn->pn_pos.begin; + PN_INIT_LIST_1(pn2, pn); + pn = pn2; + do { + pn2 = AssignExpr(cx, ts, tc); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; + } + return pn; +} + +/* 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; + JSScopeProperty *sprop; + + obj = cx->fp->scopeChain; + if (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) + return JS_TRUE; + if (InWithStatement(tc)) + return JS_TRUE; + if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, (JSProperty **)&sprop)) + return JS_FALSE; + *opp = JSOP_NAME; + *slotp = -1; + if (sprop) { + if (sprop->getter == js_GetArgument) { + *opp = JSOP_GETARG; + *slotp = JSVAL_TO_INT(sprop->id); + } else if (sprop->getter == js_GetLocalVariable) { + *opp = JSOP_GETVAR; + *slotp = JSVAL_TO_INT(sprop->id); } - if (!js_MatchToken(cx, ts, cg, TOK_COMMA)) - break; - offset = CG_OFFSET(cg); - snindex = js_NewSrcNote2(cx, cg, SRC_COMMA, 0); - if (snindex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); } return JS_TRUE; } -/* - * XXX Need to build abstract syntax tree for complete error checking: - * XXX (true ? 3 : x) = 4 does not result in an error, it evaluates to 3. - * XXX similarly for (true && x) = 5, etc. - */ -static JSBool -AssignExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - ptrdiff_t begin; - jsbytecode *pc; - JSOp lastop, op; - jsatomid atomIndex; + JSParseNode *pn, *pn2; + JSOp op; - begin = CG_OFFSET(cg); - if (!CondExpr(cx, ts, cg)) - return JS_FALSE; - if (js_MatchToken(cx, ts, cg, TOK_ASSIGN)) { - pc = CG_CODE(cg, cg->lastCodeOffset); - lastop = (JSOp) *pc; - switch (lastop) { - case JSOP_NAME: - case JSOP_GETPROP: - atomIndex = GET_ATOM_INDEX(pc); + pn = CondExpr(cx, ts, tc); + if (pn && js_MatchToken(cx, ts, TOK_ASSIGN)) { + op = ts->token.t_op; + for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid) + ; + switch (pn2->pn_type) { + case TOK_NAME: + if (pn2->pn_slot >= 0) { + PR_ASSERT(pn2->pn_op == JSOP_GETARG || pn2->pn_op == JSOP_GETVAR); + if (pn2->pn_op == JSOP_GETARG) + pn2->pn_op = JSOP_SETARG; + else + pn2->pn_op = JSOP_SETVAR; + } else { + pn2->pn_op = JSOP_SETNAME2; + } break; - case JSOP_GETELEM: - /* GETELEM and SETELEM have no immediate operand. */ + case TOK_DOT: + pn2->pn_op = JSOP_SETPROP; + break; + case TOK_LB: + pn2->pn_op = JSOP_SETELEM; break; default: js_ReportCompileError(cx, ts, "invalid assignment left-hand side"); - return JS_FALSE; - } - js_CancelLastOpcode(cx, cg, &ts->newlines); - - op = ts->token.u.op; - if (op != JSOP_NOP) { - switch (lastop) { - case JSOP_NAME: - EMIT_CONST_ATOM_OP(lastop, atomIndex); - break; - case JSOP_GETPROP: - if (js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - EMIT_CONST_ATOM_OP(lastop, atomIndex); - break; - case JSOP_GETELEM: - if (js_Emit1(cx, cg, JSOP_DUP2) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - break; - default:; - } - } - if (!AssignExpr(cx, ts, cg)) - return JS_FALSE; - if (op != JSOP_NOP) { - if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0 || - js_Emit1(cx, cg, op) < 0) { - return JS_FALSE; - } - } - - if (lastop != JSOP_NAME) { - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - (ptrdiff_t)(CG_OFFSET(cg) - begin)) < 0) { - return JS_FALSE; - } - } - - switch (lastop) { - case JSOP_NAME: - EMIT_CONST_ATOM_OP(JSOP_SETNAME, atomIndex); - break; - case JSOP_GETPROP: - EMIT_CONST_ATOM_OP(JSOP_SETPROP, atomIndex); - break; - case JSOP_GETELEM: - if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) - return JS_FALSE; - break; - default:; + return NULL; } + pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc)); } - return JS_TRUE; + return pn; } -static JSBool -CondExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - ptrdiff_t beq, jmp; + JSParseNode *pn, *pn1, *pn2, *pn3; - if (!OrExpr(cx, ts, cg)) - return JS_FALSE; - if (js_MatchToken(cx, ts, cg, TOK_HOOK)) { - if (js_NewSrcNote(cx, cg, SRC_COND) < 0) - return JS_FALSE; - beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); - if (beq < 0 || !AssignExpr(cx, ts, cg)) - return JS_FALSE; + pn = OrExpr(cx, ts, tc); + if (pn && js_MatchToken(cx, ts, TOK_HOOK)) { + pn1 = pn; + pn = NewParseNode(cx, &ts->token, PN_TERNARY); + if (!pn) + return NULL; + pn2 = AssignExpr(cx, ts, tc); + if (!pn2) + return NULL; MUST_MATCH_TOKEN(TOK_COLON, "missing : in conditional expression"); - jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); - if (jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (!AssignExpr(cx, ts, cg)) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + pn3 = AssignExpr(cx, ts, tc); + if (!pn3) + return NULL; + pn->pn_pos.begin = pn1->pn_pos.begin; + pn->pn_pos.end = pn3->pn_pos.end; + pn->pn_kid1 = pn1; + pn->pn_kid2 = pn2; + pn->pn_kid3 = pn3; } - return JS_TRUE; + return pn; } -static JSBool -OrExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { -#if JS_BUG_SHORT_CIRCUIT - ptrdiff_t beq, tmp, jmp; -#else - ptrdiff_t jmp; -#endif + JSParseNode *pn; - if (!AndExpr(cx, ts, cg)) - return JS_FALSE; - if (js_MatchToken(cx, ts, cg, TOK_OR)) { -#if JS_BUG_SHORT_CIRCUIT - beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0); - tmp = js_Emit1(cx, cg, JSOP_TRUE); - jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); - if (beq < 0 || tmp < 0 || jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); -#else - jmp = js_Emit3(cx, cg, JSOP_OR, 0, 0); - if (jmp < 0) - return JS_FALSE; -#endif - if (!OrExpr(cx, ts, cg)) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - } - return JS_TRUE; + pn = AndExpr(cx, ts, tc); + if (pn && js_MatchToken(cx, ts, TOK_OR)) + pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc)); + return pn; } -static JSBool -AndExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { -#if JS_BUG_SHORT_CIRCUIT - ptrdiff_t bne, tmp, jmp; -#else - ptrdiff_t jmp; -#endif + JSParseNode *pn; - if (!BitOrExpr(cx, ts, cg)) - return JS_FALSE; - if (js_MatchToken(cx, ts, cg, TOK_AND)) { -#if JS_BUG_SHORT_CIRCUIT - bne = js_Emit3(cx, cg, JSOP_IFNE, 0, 0); - tmp = js_Emit1(cx, cg, JSOP_FALSE); - jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0); - if (bne < 0 || tmp < 0 || jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, bne); -#else - jmp = js_Emit3(cx, cg, JSOP_AND, 0, 0); - if (jmp < 0) - return JS_FALSE; -#endif - if (!AndExpr(cx, ts, cg)) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - } - return JS_TRUE; + pn = BitOrExpr(cx, ts, tc); + if (pn && js_MatchToken(cx, ts, TOK_AND)) + pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc)); + return pn; } -static JSBool -BitOrExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - if (!BitXorExpr(cx, ts, cg)) - return JS_FALSE; - while (js_MatchToken(cx, ts, cg, TOK_BITOR)) { - if (!BitXorExpr(cx, ts, cg) || - js_Emit1(cx, cg, JSOP_BITOR) < 0) { - return JS_FALSE; - } - } - return JS_TRUE; + JSParseNode *pn; + + pn = BitXorExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_BITOR)) + pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc)); + return pn; } -static JSBool -BitXorExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - if (!BitAndExpr(cx, ts, cg)) - return JS_FALSE; - while (js_MatchToken(cx, ts, cg, TOK_BITXOR)) { - if (!BitAndExpr(cx, ts, cg) || - js_Emit1(cx, cg, JSOP_BITXOR) < 0) { - return JS_FALSE; - } - } - return JS_TRUE; + JSParseNode *pn; + + pn = BitAndExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) + pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc)); + return pn; } -static JSBool -BitAndExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - if (!EqExpr(cx, ts, cg)) - return JS_FALSE; - while (js_MatchToken(cx, ts, cg, TOK_BITAND)) { - if (!EqExpr(cx, ts, cg) || js_Emit1(cx, cg, JSOP_BITAND) < 0) - return JS_FALSE; - } - return JS_TRUE; + JSParseNode *pn; + + pn = EqExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_BITAND)) + pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc)); + return pn; } -static JSBool -EqExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { + JSParseNode *pn; JSOp op; - if (!RelExpr(cx, ts, cg)) - return JS_FALSE; - while (js_MatchToken(cx, ts, cg, TOK_EQOP)) { - op = ts->token.u.op; - if (!RelExpr(cx, ts, cg) || js_Emit1(cx, cg, op) < 0) - return JS_FALSE; + pn = RelExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_EQOP)) { + op = ts->token.t_op; + pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc)); } - return JS_TRUE; + return pn; } -static JSBool -RelExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) -{ - JSOp op; - - if (!ShiftExpr(cx, ts, cg)) - return JS_FALSE; - while (js_MatchToken(cx, ts, cg, TOK_RELOP)) { - op = ts->token.u.op; - if (!ShiftExpr(cx, ts, cg) || js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -ShiftExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) -{ - JSOp op; - - if (!AddExpr(cx, ts, cg)) - return JS_FALSE; - while (js_MatchToken(cx, ts, cg, TOK_SHOP)) { - op = ts->token.u.op; - if (!AddExpr(cx, ts, cg) || js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -AddExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { + JSParseNode *pn; JSTokenType tt; + JSOp op; - if (!MulExpr(cx, ts, cg)) - return JS_FALSE; - ts->flags |= TSF_LOOKAHEAD; - while ((tt = js_GetToken(cx, ts, cg)) == TOK_PLUS || tt == TOK_MINUS) { - ts->flags &= ~TSF_LOOKAHEAD; - if (!js_FlushNewlines(cx, ts, cg) || - !MulExpr(cx, ts, cg) || - js_Emit1(cx, cg, (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB) < 0) { + 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 +#if JS_HAS_INSTANCEOF + || js_MatchToken(cx, ts, TOK_INSTANCEOF) +#endif + )) { + tt = ts->token.type; + op = ts->token.t_op; + pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc)); + } + return pn; +} + +static JSParseNode * +ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSOp op; + + pn = AddExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_SHOP)) { + op = ts->token.t_op; + pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc)); + } + return pn; +} + +static JSParseNode * +AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSTokenType tt; + JSOp op; + + pn = MulExpr(cx, ts, tc); + while (pn && + (js_MatchToken(cx, ts, TOK_PLUS) || + js_MatchToken(cx, ts, TOK_MINUS))) { + tt = ts->token.type; + op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB; + pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc)); + } + return pn; +} + +static JSParseNode * +MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSTokenType tt; + JSOp op; + + pn = UnaryExpr(cx, ts, tc); + while (pn && + (js_MatchToken(cx, ts, TOK_STAR) || + js_MatchToken(cx, ts, TOK_DIVOP))) { + tt = ts->token.type; + op = ts->token.t_op; + pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc)); + } + return pn; +} + +static JSParseNode * +SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid, + const char *name) +{ + while (kid->pn_type == TOK_RP) + kid = kid->pn_kid; + if (kid->pn_type != TOK_NAME && + kid->pn_type != TOK_DOT && + kid->pn_type != TOK_LB) { + js_ReportCompileError(cx, ts, "invalid %s operand", name); + return NULL; + } + pn->pn_kid = kid; + return kid; +} + +static const char *incop_name_str[] = {"increment", "decrement"}; + +static JSBool +SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSParseNode *pn, JSParseNode *kid, + JSTokenType tt, JSBool preorder) +{ + jsint num; + JSOp op; + + kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]); + if (!kid) + return JS_FALSE; + num = -1; + switch (kid->pn_type) { + case TOK_NAME: + if (!LookupArgOrVar(cx, kid->pn_atom, tc, &op, &num)) return JS_FALSE; + if (op == JSOP_GETARG) { + op = (tt == TOK_INC) + ? (preorder ? JSOP_INCARG : JSOP_ARGINC) + : (preorder ? JSOP_DECARG : JSOP_ARGDEC); + } else if (op == JSOP_GETVAR) { + op = (tt == TOK_INC) + ? (preorder ? JSOP_INCVAR : JSOP_VARINC) + : (preorder ? JSOP_DECVAR : JSOP_VARDEC); + } else { + op = (tt == TOK_INC) + ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC) + : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC); } - ts->flags |= TSF_LOOKAHEAD; - } - ts->flags &= ~TSF_LOOKAHEAD; - js_UngetToken(ts); - return JS_TRUE; -} - -static JSBool -MulExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) -{ - JSOp op; - - if (!UnaryExpr(cx, ts, cg)) - return JS_FALSE; - while (js_MatchToken(cx, ts, cg, TOK_STAR) || - js_MatchToken(cx, ts, cg, TOK_DIVOP)) { - op = ts->token.u.op; - if (!UnaryExpr(cx, ts, cg) || js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -EmitIncOp(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, - JSTokenType tt, JSBool preorder) -{ - jsbytecode *pc; - JSOp op; - - pc = CG_CODE(cg, cg->lastCodeOffset); - op = (JSOp) *pc; - switch (op) { - case JSOP_NAME: - if (tt == TOK_INC) - op = preorder ? JSOP_INCNAME : JSOP_NAMEINC; - else - op = preorder ? JSOP_DECNAME : JSOP_NAMEDEC; break; - case JSOP_GETPROP: - if (tt == TOK_INC) - op = preorder ? JSOP_INCPROP : JSOP_PROPINC; - else - op = preorder ? JSOP_DECPROP : JSOP_PROPDEC; + + case TOK_DOT: + op = (tt == TOK_INC) + ? (preorder ? JSOP_INCPROP : JSOP_PROPINC) + : (preorder ? JSOP_DECPROP : JSOP_PROPDEC); break; - case JSOP_GETELEM: - if (tt == TOK_INC) - op = preorder ? JSOP_INCELEM : JSOP_ELEMINC; - else - op = preorder ? JSOP_DECELEM : JSOP_ELEMDEC; + + case TOK_LB: + op = (tt == TOK_INC) + ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC) + : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC); break; + default: - js_ReportCompileError(cx, ts, "invalid %screment expression", - (tt == TOK_INC) ? "in" : "de"); - return JS_FALSE; + PR_ASSERT(0); } - *pc = op; + pn->pn_op = op; + pn->pn_num = num; return JS_TRUE; } -static JSBool -UnaryExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSTokenType tt; - JSOp op, lastop; - ptrdiff_t begin; - jsatomid atomIndex; - uintN argc, lineno; - jsbytecode *pc; + JSParseNode *pn, *pn2, *pn3; ts->flags |= TSF_REGEXP; - tt = js_GetToken(cx, ts, cg); + tt = js_GetToken(cx, ts); ts->flags &= ~TSF_REGEXP; switch (tt) { case TOK_UNARYOP: case TOK_PLUS: case TOK_MINUS: - op = ts->token.u.op; - if (!UnaryExpr(cx, ts, cg) || js_Emit1(cx, cg, op) < 0) - return JS_FALSE; + pn = NewParseNode(cx, &ts->token, PN_UNARY); + if (!pn) + return NULL; + pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */ + pn->pn_op = ts->token.t_op; + pn2 = UnaryExpr(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_kid = pn2; break; + case TOK_INC: case TOK_DEC: - if (!MemberExpr(cx, ts, cg)) - return JS_FALSE; - if (!EmitIncOp(cx, ts, cg, tt, JS_TRUE)) - return JS_FALSE; + pn = NewParseNode(cx, &ts->token, PN_UNARY); + if (!pn) + return NULL; + pn2 = MemberExpr(cx, ts, tc); + if (!pn2) + return NULL; + if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE)) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; break; + case TOK_NEW: /* Allow 'new this.ctor(...)' constructor expressions. */ - begin = CG_OFFSET(cg); - tt = js_GetToken(cx, ts, cg); + pn = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn) + return NULL; + tt = js_GetToken(cx, ts); + pn2 = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn2) + return NULL; switch (tt) { case TOK_NAME: - atomIndex = js_IndexAtom(cx, ts->token.u.atom, &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_NAME, atomIndex); + pn2->pn_op = JSOP_NAME; + pn2->pn_atom = ts->token.t_atom; + pn2->pn_slot = -1; break; case TOK_PRIMARY: - if (ts->token.u.op == JSOP_THIS) { - if (js_Emit1(cx, cg, JSOP_THIS) < 0) - return JS_FALSE; + if (ts->token.t_op == JSOP_THIS) { + pn2->pn_op = JSOP_THIS; break; } /* FALL THROUGH */ default: js_ReportCompileError(cx, ts, "missing name after new operator"); - return JS_FALSE; + return NULL; } - while (js_MatchToken(cx, ts, cg, TOK_DOT)) { + while (js_MatchToken(cx, ts, TOK_DOT)) { + pn3 = NewParseNode(cx, &ts->token, PN_NAME); + if (!pn3) + return NULL; MUST_MATCH_TOKEN(TOK_NAME, "missing name in constructor expression"); - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - (ptrdiff_t)(CG_OFFSET(cg) - begin)) < 0) { - return JS_FALSE; - } - atomIndex = js_IndexAtom(cx, ts->token.u.atom, &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_GETPROP, atomIndex); + pn3->pn_pos.begin = pn2->pn_pos.begin; + pn3->pn_pos.end = ts->token.pos.end; + pn3->pn_op = JSOP_GETPROP; + pn3->pn_expr = pn2; + pn3->pn_atom = ts->token.t_atom; + pn2 = pn3; } - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - argc = 0; - if (js_MatchToken(cx, ts, cg, TOK_LP) && - !js_MatchToken(cx, ts, cg, TOK_RP)) { + PN_INIT_LIST_1(pn, pn2); + if (js_MatchToken(cx, ts, TOK_LP) && !js_MatchToken(cx, ts, TOK_RP)) { do { - if (!AssignExpr(cx, ts, cg)) - return JS_FALSE; - argc++; - } while (js_MatchToken(cx, ts, cg, TOK_COMMA)); + pn2 = AssignExpr(cx, ts, tc); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); /* (balance: */ MUST_MATCH_TOKEN(TOK_RP, "missing ) after constructor argument list"); } - if (argc >= ARGC_LIMIT) { + if (pn->pn_count - 1 >= ARGC_LIMIT) { JS_ReportError(cx, "too many constructor arguments"); - return JS_FALSE; + return NULL; } - if (js_Emit3(cx, cg, JSOP_NEW, ARGC_HI(argc), ARGC_LO(argc)) < 0) - return JS_FALSE; + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; break; case TOK_DELETE: - /* Compile the operand expression. */ - begin = CG_OFFSET(cg); - if (!MemberExpr(cx, ts, cg)) - return JS_FALSE; - - /* Cancel the last bytecode, saving it for re-emission later. */ - pc = CG_CODE(cg, cg->lastCodeOffset); - lastop = (JSOp) *pc; - switch (lastop) { - case JSOP_NAME: - case JSOP_GETPROP: - atomIndex = GET_ATOM_INDEX(pc); - break; - case JSOP_GETELEM: - /* GETELEM and DELELEM have no immediate operand. */ - break; - default: - js_ReportCompileError(cx, ts, "invalid %s operand", js_delete_str); - return JS_FALSE; - } - js_CancelLastOpcode(cx, cg, &ts->newlines); - - if (lastop != JSOP_NAME) { - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - (int16)(CG_OFFSET(cg) - begin)) < 0) { - return JS_FALSE; - } - } - - /* Re-emit the operand's last bytecode as a JSOP_DEL* variant. */ - switch (lastop) { - case JSOP_NAME: - EMIT_CONST_ATOM_OP(JSOP_DELNAME, atomIndex); - break; - case JSOP_GETPROP: - EMIT_CONST_ATOM_OP(JSOP_DELPROP, atomIndex); - break; - case JSOP_GETELEM: - if (js_Emit1(cx, cg, JSOP_DELELEM) < 0) - return JS_FALSE; - break; - default:; - } + pn = NewParseNode(cx, &ts->token, PN_UNARY); + if (!pn) + return NULL; + pn2 = MemberExpr(cx, ts, tc); + if (!pn2) + return NULL; + if (!SetLvalKid(cx, ts, pn, pn2, js_delete_str)) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; break; case TOK_ERROR: - return JS_FALSE; + return NULL; default: js_UngetToken(ts); - lineno = ts->lineno; - if (!MemberExpr(cx, ts, cg)) - return JS_FALSE; + pn = MemberExpr(cx, ts, tc); + if (!pn) + return NULL; /* Don't look across a newline boundary for a postfix incop. */ - if (ts->lineno == lineno && - (js_MatchToken(cx, ts, cg, TOK_INC) || - js_MatchToken(cx, ts, cg, TOK_DEC))) { - if (!EmitIncOp(cx, ts, cg, ts->token.type, JS_FALSE)) - return JS_FALSE; + if (pn->pn_pos.end.lineno == ts->lineno) { + tt = js_PeekTokenSameLine(cx, ts); + if (tt == TOK_INC || tt == TOK_DEC) { + (void) js_GetToken(cx, ts); + pn2 = NewParseNode(cx, &ts->token, PN_UNARY); + if (!pn2) + return NULL; + if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE)) + return NULL; + pn2->pn_pos.begin = pn->pn_pos.begin; + pn = pn2; + } } + break; } - return JS_TRUE; + return pn; } -static JSBool -MemberExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - ptrdiff_t begin; - JSToken token; + JSParseNode *pn, *pn2, *pn3; JSTokenType tt; - jsatomid atomIndex; - uintN argc; JSBool matched; - JSObject *pobj; - JSProperty *prop; - jsval val; - JSFunction *fun; - char *option; - JSAtom *atom; - begin = CG_OFFSET(cg); - if (!PrimaryExpr(cx, ts, cg)) - return JS_FALSE; - - token = ts->token; - ts->flags |= TSF_LOOKAHEAD; - while ((tt = js_GetToken(cx, ts, cg)) > TOK_EOF) { - ts->flags &= ~TSF_LOOKAHEAD; + pn = PrimaryExpr(cx, ts, tc); + if (!pn) + return NULL; + while ((tt = js_GetToken(cx, ts)) > TOK_EOF) { if (tt == TOK_DOT) { - /* - * Compile get-property opcode. - */ - if (!js_FlushNewlines(cx, ts, cg)) - return JS_FALSE; + pn2 = NewParseNode(cx, &ts->token, PN_NAME); + if (!pn2) + return NULL; MUST_MATCH_TOKEN(TOK_NAME, "missing name after . operator"); - - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - (ptrdiff_t)(CG_OFFSET(cg) - begin)) < 0) { - return JS_FALSE; - } - atomIndex = js_IndexAtom(cx, ts->token.u.atom, &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_GETPROP, atomIndex); + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = ts->token.pos.end; + pn2->pn_op = JSOP_GETPROP; + pn2->pn_expr = pn; + pn2->pn_atom = ts->token.t_atom; } else if (tt == TOK_LB) { - /* - * Compile index expression and get-slot opcode. - */ - if (!js_FlushNewlines(cx, ts, cg)) - return JS_FALSE; - if (!Expr(cx, ts, cg)) - return JS_FALSE; + pn2 = NewParseNode(cx, &ts->token, PN_BINARY); + if (!pn2) + return NULL; + pn3 = Expr(cx, ts, tc); + if (!pn3) + return NULL; + /* [balance: */ MUST_MATCH_TOKEN(TOK_RB, "missing ] in index expression"); + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = ts->token.pos.end; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - (ptrdiff_t)(CG_OFFSET(cg) - begin)) < 0) { - return JS_FALSE; + /* Optimize o['p'] to o.p by rewriting pn2. */ + if (pn3->pn_type == TOK_STRING) { + pn2->pn_type = TOK_DOT; + pn2->pn_op = JSOP_GETPROP; + pn2->pn_arity = PN_NAME; + pn2->pn_expr = pn; + pn2->pn_atom = pn3->pn_atom; + } else { + pn2->pn_op = JSOP_GETELEM; + pn2->pn_left = pn; + pn2->pn_right = pn3; } - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; } else if (tt == TOK_LP) { - /* - * Compile 'this' and the actual args, followed by a call opcode. - */ - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - if (!js_FlushNewlines(cx, ts, cg)) - return JS_FALSE; - argc = 0; + pn2 = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn2) + return NULL; + PN_INIT_LIST_1(pn2, pn); + pn2->pn_op = JSOP_CALL; + ts->flags |= TSF_REGEXP; - matched = js_MatchToken(cx, ts, cg, TOK_RP); + matched = js_MatchToken(cx, ts, TOK_RP); ts->flags &= ~TSF_REGEXP; if (!matched) { do { - if (!AssignExpr(cx, ts, cg)) - return JS_FALSE; - argc++; - } while (js_MatchToken(cx, ts, cg, TOK_COMMA)); + pn3 = AssignExpr(cx, ts, tc); + if (!pn3) + return NULL; + PN_APPEND(pn2, pn3); + } while (js_MatchToken(cx, ts, TOK_COMMA)); /* (balance: */ MUST_MATCH_TOKEN(TOK_RP, "missing ) after argument list"); } - if (argc >= ARGC_LIMIT) { + if (pn2->pn_count - 1 >= ARGC_LIMIT) { JS_ReportError(cx, "too many function arguments"); - return JS_FALSE; + return NULL; } - if (js_Emit3(cx, cg, JSOP_CALL, ARGC_HI(argc), ARGC_LO(argc)) < 0) - return JS_FALSE; + pn2->pn_pos.end = PN_LAST(pn2)->pn_pos.end; } else { js_UngetToken(ts); - - /* - * Look for a name, number, or string immediately after a name. - * Such a token juxtaposition is likely to be a "command-style" - * function call. - */ - if ((ts->flags & TSF_COMMAND) && - token.type == TOK_NAME && - *CG_CODE(cg, cg->lastCodeOffset) == JSOP_NAME && - (tt == TOK_EOL || tt == TOK_MINUS || IS_PRIMARY_TOKEN(tt))) { - pobj = NULL; - if (!js_LookupProperty(cx, cx->fp->scopeChain, - (jsval)token.u.atom, &pobj, &prop)) { - return JS_FALSE; - } - if (prop && - (val = prop->object->slots[prop->slot]) && - JSVAL_IS_FUNCTION(val) && - (fun = js_ValueToFunction(cx, val)) && - (tt != TOK_EOL || fun->nargs == 0)) { - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - if (!js_FlushNewlines(cx, ts, cg)) - return JS_FALSE; - argc = 0; - while (tt != TOK_EOL && tt != TOK_SEMI && - tt != TOK_RP && tt != TOK_RC) { - if (tt == TOK_MINUS && JS7_ISLET(ts->token.ptr[1])) { - (void) js_GetToken(cx, ts, cg); - MUST_MATCH_TOKEN(TOK_NAME, - "missing option after - in command"); - option = PR_smprintf("-%s", - ATOM_BYTES(ts->token.u.atom)); - if (!option) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - atom = js_Atomize(cx, option, strlen(option), 0); - free(option); - if (!atom) - return JS_FALSE; - atomIndex = js_IndexAtom(cx, atom, &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_STRING, atomIndex); - js_DropAtom(cx, atom); - } else { - if (!AssignExpr(cx, ts, cg)) - return JS_FALSE; - } - argc++; - js_MatchToken(cx, ts, cg, TOK_COMMA); - tt = js_PeekToken(cx, ts, cg); - } - if (argc >= ARGC_LIMIT) { - JS_ReportError(cx, "too many command arguments"); - return JS_FALSE; - } - if (js_Emit3(cx, cg, JSOP_CALL, - ARGC_HI(argc), ARGC_LO(argc)) < 0) { - return JS_FALSE; - } - } - } - break; + return pn; } - token = ts->token; - ts->flags |= TSF_LOOKAHEAD; + pn = pn2; } - ts->flags &= ~TSF_LOOKAHEAD; - return (tt != TOK_ERROR); + if (tt == TOK_ERROR) + return NULL; + return pn; } -static JSBool -EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) -{ - jsint ival; - JSAtom *atom; - jsatomid atomIndex; - - 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; - if (ival == 1) - return js_Emit1(cx, cg, JSOP_ONE) >= 0; - if ((jsuint)ival < (jsuint)ATOM_INDEX_LIMIT) { - atomIndex = (jsatomid)ival; - EMIT_CONST_ATOM_OP(JSOP_UINT16, atomIndex); - return JS_TRUE; - } - atom = js_AtomizeInt(cx, ival, ATOM_NOHOLD); - } else { - atom = js_AtomizeDouble(cx, dval, ATOM_NOHOLD); - } - if (!atom) - return JS_FALSE; - atomIndex = js_IndexAtom(cx, atom, &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_NUMBER, atomIndex); - return JS_TRUE; -} - -static JSBool -PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +static JSParseNode * +PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSTokenType tt; - jsatomid atomIndex; + JSParseNode *pn, *pn2, *pn3; #if JS_HAS_SHARP_VARS - JSBool defsharp = JS_FALSE; - jsatomid sharpnum = 0; + JSParseNode *defsharp; + JSBool notsharp; + + defsharp = NULL; + notsharp = JS_FALSE; again: + /* + * Control flows here after #n= is scanned. If the following primary is + * not valid after such a "sharp variable" definition, the token type case + * should set notsharp. + */ #endif ts->flags |= TSF_REGEXP; - tt = js_GetToken(cx, ts, cg); + tt = js_GetToken(cx, ts); ts->flags &= ~TSF_REGEXP; switch (tt) { #if JS_HAS_LEXICAL_CLOSURE case TOK_FUNCTION: - return FunctionExpr(cx, ts, cg); + pn = FunctionExpr(cx, ts, tc); + if (!pn) + return NULL; + break; #endif -#if JS_HAS_OBJECT_LITERAL +#if JS_HAS_INITIALIZERS case TOK_LB: { JSBool matched; - jsint index; + jsint atomIndex; - atomIndex = js_IndexAtom(cx, cx->runtime->atomState.ArrayAtom, - &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_NAME, atomIndex); - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) - return JS_FALSE; + pn = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn) + return NULL; + pn->pn_type = TOK_RB; + pn->pn_extra = JS_FALSE; #if JS_HAS_SHARP_VARS - if (defsharp) - EMIT_CONST_ATOM_OP(JSOP_DEFSHARP, sharpnum); + if (defsharp) { + PN_INIT_LIST_1(pn, defsharp); + defsharp = NULL; + } else #endif + PN_INIT_LIST(pn); ts->flags |= TSF_REGEXP; - matched = js_MatchToken(cx, ts, cg, TOK_RB); + matched = js_MatchToken(cx, ts, TOK_RB); ts->flags &= ~TSF_REGEXP; if (!matched) { - for (index = 0; index < ATOM_INDEX_LIMIT; index++) { + for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) { ts->flags |= TSF_REGEXP; - tt = js_PeekToken(cx, ts, cg); + tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_REGEXP; if (tt == TOK_RB) { - if (js_NewSrcNote(cx, cg, SRC_COMMA) < 0) - return JS_FALSE; + pn->pn_extra = JS_TRUE; break; } - if (index == 0) { - if (js_Emit1(cx, cg, JSOP_ZERO) < 0) - return JS_FALSE; - } else if (index == 1) { - if (js_Emit1(cx, cg, JSOP_ONE) < 0) - return JS_FALSE; - } else { - atomIndex = (jsatomid) index; - EMIT_CONST_ATOM_OP(JSOP_UINT16, atomIndex); - } + if (tt == TOK_COMMA) + pn2 = NewParseNode(cx, &ts->token, PN_NULLARY); + else + pn2 = AssignExpr(cx, ts, tc); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); - if (tt == TOK_COMMA) { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } else { - if (!AssignExpr(cx, ts, cg)) - return JS_FALSE; - } - if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) - return JS_FALSE; - - if (!js_MatchToken(cx, ts, cg, TOK_COMMA)) + if (!js_MatchToken(cx, ts, TOK_COMMA)) break; } /* [balance: */ MUST_MATCH_TOKEN(TOK_RB, "missing ] after element list"); } - - /* Emit an annotated op for sharpArray cleanup and decompilation. */ - if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) - return JS_FALSE; - break; + pn->pn_pos.end = ts->token.pos.end; + return pn; } case TOK_LC: - { - atomIndex = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom, - &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_NAME, atomIndex); - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) - return JS_FALSE; + pn = NewParseNode(cx, &ts->token, PN_LIST); + if (!pn) + return NULL; + pn->pn_type = TOK_RC; #if JS_HAS_SHARP_VARS - if (defsharp) - EMIT_CONST_ATOM_OP(JSOP_DEFSHARP, sharpnum); + if (defsharp) { + PN_INIT_LIST_1(pn, defsharp); + defsharp = NULL; + } else #endif + PN_INIT_LIST(pn); - if (!js_MatchToken(cx, ts, cg, TOK_RC)) { + if (!js_MatchToken(cx, ts, TOK_RC)) { do { - tt = js_GetToken(cx, ts, cg); + tt = js_GetToken(cx, ts); switch (tt) { case TOK_NUMBER: - if (!EmitNumberOp(cx, ts->token.u.dval, cg)) - return JS_FALSE; + pn3 = NewParseNode(cx, &ts->token, PN_NULLARY); + if (pn3) + pn3->pn_dval = ts->token.t_dval; break; case TOK_NAME: case TOK_STRING: - atomIndex = js_IndexAtom(cx, ts->token.u.atom, - &cg->atomList); + pn3 = NewParseNode(cx, &ts->token, PN_NULLARY); + if (pn3) + pn3->pn_atom = ts->token.t_atom; break; + case TOK_RC: + goto end_obj_init; default: js_ReportCompileError(cx, ts, "invalid property id"); - return JS_FALSE; + return NULL; } MUST_MATCH_TOKEN(TOK_COLON, "missing : after property id"); - if (!AssignExpr(cx, ts, cg)) - return JS_FALSE; - - if (tt == TOK_NUMBER) { - if (js_NewSrcNote(cx, cg, SRC_LABEL) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) - return JS_FALSE; - } else { - EMIT_CONST_ATOM_OP(JSOP_INITPROP, atomIndex); - } - } while (js_MatchToken(cx, ts, cg, TOK_COMMA)); + pn2 = NewBinary(cx, TOK_COLON, JSOP_INITPROP, pn3, + AssignExpr(cx, ts, tc)); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); /* {balance: */ MUST_MATCH_TOKEN(TOK_RC, "missing } after property list"); } - - /* Emit an op for sharpArray cleanup and decompilation. */ - if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) - return JS_FALSE; - break; - } + end_obj_init: + pn->pn_pos.end = ts->token.pos.end; + return pn; #if JS_HAS_SHARP_VARS case TOK_DEFSHARP: - defsharp = JS_TRUE; - sharpnum = (jsatomid) ts->token.u.dval; - ts->flags |= TSF_REGEXP; - tt = js_PeekToken(cx, ts, cg); - ts->flags &= ~TSF_REGEXP; - if (tt != TOK_LB && tt != TOK_LC) { - js_ReportCompileError(cx, ts, "invalid sharp variable definition"); - return JS_FALSE; - } + if (defsharp) + goto badsharp; + defsharp = NewParseNode(cx, &ts->token, PN_UNARY); + if (!defsharp) + return NULL; + defsharp->pn_num = (jsint) ts->token.t_dval; + defsharp->pn_kid = NULL; goto again; case TOK_USESHARP: /* Check for forward/dangling references at runtime, to allow eval. */ - sharpnum = (jsatomid) ts->token.u.dval; - EMIT_CONST_ATOM_OP(JSOP_USESHARP, sharpnum); + pn = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn) + return NULL; + pn->pn_num = (jsint) ts->token.t_dval; + notsharp = JS_TRUE; break; #endif /* JS_HAS_SHARP_VARS */ -#endif /* JS_HAS_OBJECT_LITERAL */ +#endif /* JS_HAS_INITIALIZERS */ case TOK_LP: - if (!Expr(cx, ts, cg)) - return JS_FALSE; + pn = NewParseNode(cx, &ts->token, PN_UNARY); + if (!pn) + return NULL; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + /* (balance: */ MUST_MATCH_TOKEN(TOK_RP, "missing ) in parenthetical"); + pn->pn_type = TOK_RP; + pn->pn_pos.end = ts->token.pos.end; + pn->pn_kid = pn2; + break; - /* Emit an annotated NOP so we can decompile user parens. */ - if (js_NewSrcNote(cx, cg, SRC_PAREN) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; + case TOK_STRING: + notsharp = JS_TRUE; + /* FALL THROUGH */ + case TOK_NAME: + case TOK_OBJECT: + pn = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn) + return NULL; + pn->pn_op = ts->token.t_op; + pn->pn_atom = ts->token.t_atom; + pn->pn_slot = -1; + if (tt == TOK_NAME) { + if (!LookupArgOrVar(cx, pn->pn_atom, tc, &pn->pn_op, &pn->pn_slot)) + return NULL; + pn->pn_arity = PN_NAME; + pn->pn_expr = NULL; } break; - case TOK_NAME: - atomIndex = js_IndexAtom(cx, ts->token.u.atom, &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_NAME, atomIndex); - break; - case TOK_NUMBER: - return EmitNumberOp(cx, ts->token.u.dval, cg); - - case TOK_STRING: - atomIndex = js_IndexAtom(cx, ts->token.u.atom, &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_STRING, atomIndex); - break; - - case TOK_OBJECT: - atomIndex = js_IndexAtom(cx, ts->token.u.atom, &cg->atomList); - EMIT_CONST_ATOM_OP(JSOP_OBJECT, atomIndex); + pn = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn) + return NULL; + pn->pn_dval = ts->token.t_dval; + notsharp = JS_TRUE; break; case TOK_PRIMARY: - if (js_Emit1(cx, cg, ts->token.u.op) < 0) - return JS_FALSE; + pn = NewParseNode(cx, &ts->token, PN_NULLARY); + if (!pn) + return NULL; + pn->pn_op = ts->token.t_op; + notsharp = JS_TRUE; break; #if !JS_HAS_EXPORT_IMPORT @@ -2440,16 +2335,251 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) case TOK_IMPORT: #endif case TOK_RESERVED: - js_ReportCompileError(cx, ts, "identifier is a reserved word"); - return JS_FALSE; + js_ReportCompileError(cx, ts, "%.*s is a reserved identifier", + ts->token.pos.end.index - ts->token.pos.begin.index, + ts->token.ptr); + return NULL; case TOK_ERROR: /* The scanner or one of its subroutines reported the error. */ - return JS_FALSE; + return NULL; default: js_ReportCompileError(cx, ts, "syntax error"); - return JS_FALSE; + return NULL; } + +#if JS_HAS_SHARP_VARS + if (defsharp) { + if (notsharp) { + badsharp: + js_ReportCompileError(cx, ts, "invalid sharp variable definition"); + return NULL; + } + defsharp->pn_kid = pn; + return defsharp; + } +#endif + return pn; +} + +JSBool +js_FoldConstants(JSContext *cx, JSParseNode *pn) +{ + JSParseNode *pn1, *pn2, *pn3; + + switch (pn->pn_arity) { + case PN_FUNC: + if (!js_FoldConstants(cx, pn->pn_body)) + return JS_FALSE; + break; + + case PN_LIST: + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (!js_FoldConstants(cx, pn2)) + return JS_FALSE; + } + break; + + case PN_TERNARY: + /* Any kid may be null (e.g. for (;;)). */ + pn1 = pn->pn_kid1; + pn2 = pn->pn_kid2; + pn3 = pn->pn_kid3; + if (pn1 && !js_FoldConstants(cx, pn1)) + return JS_FALSE; + if (pn2 && !js_FoldConstants(cx, pn2)) + return JS_FALSE; + if (pn3 && !js_FoldConstants(cx, pn3)) + return JS_FALSE; + break; + + case PN_BINARY: + /* Neither kid may be null. */ + pn1 = pn->pn_left; + pn2 = pn->pn_right; + if (!js_FoldConstants(cx, pn1)) + return JS_FALSE; + if (!js_FoldConstants(cx, pn2)) + return JS_FALSE; + break; + + case PN_UNARY: + /* Our kid may be null (e.g. return; vs. return e;). */ + pn1 = pn->pn_kid; + if (pn1 && !js_FoldConstants(cx, pn1)) + return JS_FALSE; + break; + + case PN_NAME: + pn1 = pn->pn_expr; + if (pn1 && !js_FoldConstants(cx, pn1)) + return JS_FALSE; + break; + + case PN_NULLARY: + break; + } + + switch (pn->pn_type) { + case TOK_PLUS: + if (pn1->pn_type == TOK_STRING && pn2->pn_type == TOK_STRING) { + JSString *str1, *str2; + size_t length, length1, length2, nbytes; + void *mark; + jschar *chars; + + /* Concatenate string constants. */ + str1 = ATOM_TO_STRING(pn1->pn_atom); + str2 = ATOM_TO_STRING(pn2->pn_atom); + length1 = str1->length; + length2 = str2->length; + length = length1 + length2; + nbytes = (length + 1) * sizeof(jschar); + mark = PR_ARENA_MARK(&cx->tempPool); + PR_ARENA_ALLOCATE(chars, &cx->tempPool, nbytes); + if (!chars) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + js_strncpy(chars, str1->chars, length1); + js_strncpy(chars + length1, str2->chars, length2); + chars[length] = 0; + pn->pn_atom = js_AtomizeChars(cx, chars, length, 0); + if (!pn->pn_atom) + return JS_FALSE; + PR_ARENA_RELEASE(&cx->tempPool, mark); + pn->pn_type = TOK_STRING; + pn->pn_op = JSOP_STRING; + pn->pn_arity = PN_NULLARY; + break; + } + /* FALL THROUGH */ + + case TOK_SHOP: + case TOK_MINUS: + case TOK_STAR: + case TOK_DIVOP: + if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) { + jsdouble d, d2; + int32 i, j; + uint32 u; + + /* Fold two numeric constants. */ + d = pn1->pn_dval; + d2 = pn2->pn_dval; + switch (pn->pn_op) { + case JSOP_LSH: + case JSOP_RSH: + if (!js_DoubleToECMAInt32(cx, d, &i)) + return JS_FALSE; + if (!js_DoubleToECMAInt32(cx, d2, &j)) + return JS_FALSE; + j &= 31; + d = (pn->pn_op == JSOP_LSH) ? i << j : i >> j; + break; + + case JSOP_URSH: + if (!js_DoubleToECMAUint32(cx, d, &u)) + return JS_FALSE; + if (!js_DoubleToECMAInt32(cx, d2, &j)) + return JS_FALSE; + j &= 31; + d = u >> j; + break; + + case JSOP_ADD: + d += d2; + break; + + case JSOP_SUB: + d -= d2; + break; + + case JSOP_MUL: + d *= d2; + break; + + case JSOP_DIV: + if (d2 == 0) { +#ifdef XP_PC + /* XXX MSVC miscompiles such that (NaN == 0) */ + if (JSDOUBLE_IS_NaN(d2)) + d = *cx->runtime->jsNaN; + else +#endif + if (d == 0 || JSDOUBLE_IS_NaN(d)) + d = *cx->runtime->jsNaN; + else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) + d = *cx->runtime->jsNegativeInfinity; + else + d = *cx->runtime->jsPositiveInfinity; + } else { + d /= d2; + } + break; + + case JSOP_MOD: + if (d2 == 0) { + d = *cx->runtime->jsNaN; + } else { +#ifdef XP_PC + /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ + if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) +#endif + d = fmod(d, d2); + } + break; + + default:; + } + pn->pn_type = TOK_NUMBER; + pn->pn_op = JSOP_NUMBER; + pn->pn_arity = PN_NULLARY; + pn->pn_dval = d; + } + break; + + case TOK_UNARYOP: + if (pn1->pn_type == TOK_NUMBER) { + jsdouble d; + int32 i; + + /* Operate on one numeric constants. */ + d = pn1->pn_dval; + switch (pn->pn_op) { + case JSOP_BITNOT: + if (!js_DoubleToECMAInt32(cx, d, &i)) + return JS_FALSE; + d = ~i; + break; + + case JSOP_NEG: + d = -d; + break; + + case JSOP_POS: + break; + + case JSOP_NOT: + pn->pn_type = TOK_PRIMARY; + pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE; + pn->pn_arity = PN_NULLARY; + /* FALL THROUGH */ + + default: + /* Return early to dodge the common TOK_NUMBER code. */ + return JS_TRUE; + } + pn->pn_type = TOK_NUMBER; + pn->pn_op = JSOP_NUMBER; + pn->pn_arity = PN_NULLARY; + pn->pn_dval = d; + } + break; + + default:; + } + return JS_TRUE; } diff --git a/mozilla/js/src/jsparse.h b/mozilla/js/src/jsparse.h index 9322c251f99..da0f2f02c80 100644 --- a/mozilla/js/src/jsparse.h +++ b/mozilla/js/src/jsparse.h @@ -23,16 +23,230 @@ */ #include "jsprvtd.h" #include "jspubtd.h" +#include "jsscan.h" PR_BEGIN_EXTERN_C +/* + * Parsing builds a tree of nodes that directs code generation. This tree is + * not a concrete syntax tree in all respects (for example, || and && are left + * associative, but (A && B && C) translates into the right-associated tree + * > so that code generation can emit a left-associative branch + * around when A is false). Nodes are labeled by token type, with a + * JSOp secondary label when needed: + * + * Label Variant Members + * ----- ------- ------- + * + * TOK_FUNCTION func pn_fun: function, contains arg and var properties + * pn_body: TOK_LC node for function body + * NB: We define or create the function object at + * parse (not emit) time, in order to specialize arg + * and var bytecodes early. + * pn_tryCount: of try statements in function + * + * + * TOK_LC list pn_head: list of pn_count statements + * TOK_EXPORT list pn_head: list of pn_count TOK_NAMEs or one TOK_STAR + * (which is not a multiply node) + * TOK_IMPORT list pn_head: list of pn_count sub-trees of the form + * a.b.*, a[b].*, a.*, a.b, or a[b] -- but never a. + * Each member is expressed with TOK_DOT or TOK_LB. + * Each sub-tree's root node has a pn_op in the set + * JSOP_IMPORT{ALL,PROP,ELEM} + * TOK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null + * TOK_SWITCH ternary pn_kid1: discriminant + * pn_kid2: list of TOK_CASE nodes + * pn_kid3: TOK_LC node for default statements or null + * TOK_CASE binary pn_left: case expr + * pn_right: TOK_LC node for case statements + * TOK_WHILE binary pn_left: cond, pn_right: body + * TOK_DO binary pn_left: body, pn_right: cond + * TOK_FOR binary pn_left: either + * for/in loop: a binary TOK_IN node with + * pn_left: TOK_VAR or TOK_NAME to left of 'in' + * pn_right: object expr to right of 'in' + * for(;;) loop: a ternary TOK_RESERVED node with + * pn_kid1: init expr before first ';' + * pn_kid2: cond expr before second ';' + * pn_kid3: update expr after second ';' + * any kid may be null + * pn_right: body + * TOK_THROW unary pn_op: JSOP_THROW, pn_kid: exception + * TOK_BREAK name pn_atom: label or null + * TOK_CONTINUE name pn_atom: label or null + * TOK_WITH binary pn_left: head expr, pn_right: body + * TOK_VAR list pn_head: list of pn_count TOK_NAME nodes + * each name node has pn_atom: variable name and + * pn_expr: initializer or null + * TOK_RETURN unary pn_kid: return expr or null + * TOK_SEMI unary pn_kid: expr or null statement + * TOK_COLON name pn_atom: label, pn_expr: labeled statement + * + * + * TOK_COMMA list pn_head: list of pn_count comma-separated exprs + * TOK_ASSIGN binary pn_left: lvalue, pn_right: rvalue + * TOK_HOOK ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else + * TOK_OR binary pn_left: first in || chain, pn_right: rest of chain + * TOK_AND binary pn_left: first in && chain, pn_right: rest of chain + * TOK_BITOR binary pn_left: left-assoc | expr, pn_right: ^ expr + * TOK_BITXOR binary pn_left: left-assoc ^ expr, pn_right: & expr + * TOK_BITAND binary pn_left: left-assoc & expr, pn_right: EQ expr + * TOK_EQOP binary pn_left: left-assoc EQ expr, pn_right: REL expr + * pn_op: JSOP_EQ, JSOP_NE, JSOP_NEW_EQ, JSOP_NEW_NE + * TOK_RELOP binary pn_left: left-assoc REL expr, pn_right: SH expr + * pn_op: JSOP_LT, JSOP_LE, JSOP_GT, JSOP_GE + * TOK_SHOP binary pn_left: left-assoc SH expr, pn_right: ADD expr + * pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH + * TOK_PLUS, binary pn_left: left-assoc ADD expr, pn_right: MUL expr + * TOK_MINUS pn_op: JSOP_ADD, JSOP_SUB + * TOK_STAR, binary pn_left: left-assoc MUL expr, pn_right: UNARY expr + * TOK_DIVOP pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD + * TOK_UNARYOP unary pn_kid: UNARY expr, pn_op: JSOP_NEG, JSOP_POS, + * JSOP_NOT, JSOP_BITNOT, JSOP_TYPEOF, JSOP_VOID + * TOK_INC, unary pn_kid: MEMBER expr + * TOK_DEC pn_num: arg or local var slot if non-negative + * TOK_NEW list pn_head: list of ctor, arg1, arg2, ... argN + * pn_count: 1 + N (where N is number of args) + * ctor is a restricted MEMBER expr (XXX not ECMA) + * TOK_DELETE unary pn_kid: MEMBER expr + * TOK_DOT name pn_expr: MEMBER expr to left of . + * pn_atom: name to right of . + * TOK_LB binary pn_left: MEMBER expr to left of [ + * pn_right: expr between [ and ] + * TOK_LP list pn_head: list of call, arg1, arg2, ... argN + * pn_count: 1 + N (where N is number of args) + * call is a MEMBER expr naming a callable object + * TOK_RB list pn_head: list of pn_count array element exprs + * [,,] holes are represented by TOK_COMMA nodes + * #n=[...] produces TOK_DEFSHARP at head of list + * pn_extra: true if extra comma at end + * TOK_RC list pn_head: list of pn_count TOK_COLON nodes where + * each has pn_left: property id, pn_right: value + * #n={...} produces TOK_DEFSHARP at head of list + * TOK_DEFSHARP unary pn_num: jsint value of n in #n= + * TOK_USESHARP nullary pn_num: jsint value of n in #n# + * TOK_RP unary pn_kid: parenthesized expression + * TOK_NAME, name pn_atom: name, string, or object atom + * TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT + * TOK_OBJECT pn_op may be *ARG or *VAR with pn_slot >= 0 + * TOK_NUMBER dval pn_dval: double value of numeric literal + * TOK_PRIMARY nullary pn_op: JSOp bytecode + */ +typedef enum JSParseNodeArity { + PN_FUNC = -3, + PN_LIST = -2, + PN_TERNARY = 3, + PN_BINARY = 2, + PN_UNARY = 1, + PN_NAME = -1, + PN_NULLARY = 0 +} JSParseNodeArity; + +struct JSParseNode { + JSTokenType pn_type; + JSTokenPos pn_pos; + JSOp pn_op; + ptrdiff_t pn_offset; /* first generated bytecode offset */ + JSParseNodeArity pn_arity; + union { + struct { /* TOK_FUNCTION node */ + JSFunction *fun; /* function object private data */ + JSParseNode *body; /* TOK_LC list of statements */ + uint32 tryCount; /* try statement count */ + } func; + struct { /* list of next-linked nodes */ + JSParseNode *head; /* first node in list */ + JSParseNode **tail; /* ptr to ptr to last node in list */ + uint32 count; /* number of nodes in list */ + JSBool extra; /* extra comma flag for [1,2,,] */ + } list; + struct { /* ternary: if, switch, for(;;), ?: */ + JSParseNode *kid1; /* condition, discriminant, etc. */ + JSParseNode *kid2; /* then-part, case list, etc. */ + JSParseNode *kid3; /* else-part, default case, etc. */ + } ternary; + struct { /* two kids if binary */ + JSParseNode *left; + JSParseNode *right; + jsval val; /* switch case value */ + } binary; + struct { /* one kid if unary */ + JSParseNode *kid; + jsint num; /* -1 or arg or local/sharp var num */ + } unary; + struct { /* name, labeled statement, etc. */ + JSAtom *atom; /* name or label atom, null if slot */ + JSParseNode *expr; /* object or initializer */ + jsint slot; /* -1 or arg or local var slot */ + } name; + jsdouble dval; /* aligned numeric literal value */ + } pn_u; + JSParseNode *pn_next; /* here to align dval and pn_u on RISCs */ +}; + +#define pn_fun pn_u.func.fun +#define pn_body pn_u.func.body +#define pn_tryCount pn_u.func.tryCount +#define pn_head pn_u.list.head +#define pn_tail pn_u.list.tail +#define pn_count pn_u.list.count +#define pn_extra pn_u.list.extra +#define pn_kid1 pn_u.ternary.kid1 +#define pn_kid2 pn_u.ternary.kid2 +#define pn_kid3 pn_u.ternary.kid3 +#define pn_left pn_u.binary.left +#define pn_right pn_u.binary.right +#define pn_val pn_u.binary.val +#define pn_kid pn_u.unary.kid +#define pn_num pn_u.unary.num +#define pn_atom pn_u.name.atom +#define pn_expr pn_u.name.expr +#define pn_slot pn_u.name.slot +#define pn_dval pn_u.dval + +/* + * Compute a pointer to the last JSParseNode element in a singly-linked list. + * NB: list must be non-empty for correct PN_LAST usage! + */ +#define PN_LAST(list) \ + ((JSParseNode *)((char *)(list)->pn_tail - offsetof(JSParseNode, pn_next))) + +#define PN_INIT_LIST(list) \ + PR_BEGIN_MACRO \ + (list)->pn_head = NULL; \ + (list)->pn_tail = &(list)->pn_head; \ + (list)->pn_count = 0; \ + PR_END_MACRO + +#define PN_INIT_LIST_1(list, pn) \ + PR_BEGIN_MACRO \ + (list)->pn_head = (pn); \ + (list)->pn_tail = &(pn)->pn_next; \ + (list)->pn_count = 1; \ + PR_END_MACRO + +#define PN_APPEND(list, pn) \ + PR_BEGIN_MACRO \ + *(list)->pn_tail = (pn); \ + (list)->pn_tail = &(pn)->pn_next; \ + (list)->pn_count++; \ + PR_END_MACRO + +/* New names to connote code generation in addition to parse tree gen. */ +#define js_CompileTokenStream js_Parse +#define js_CompileFunctionBody js_ParseFunctionBody + extern JS_FRIEND_API(JSBool) -js_Parse(JSContext *cx, JSObject *slink, JSTokenStream *ts, - JSCodeGenerator *cg); +js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, + JSCodeGenerator *cg); extern JSBool -js_ParseFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, - JSSymbol *args); +js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun); + +/* XXXbe expose js_Parse{TokenStream,FunctionBody} that return trees? */ +extern JSBool +js_FoldConstants(JSContext *cx, JSParseNode *pn); PR_END_EXTERN_C diff --git a/mozilla/js/src/jsprvtd.h b/mozilla/js/src/jsprvtd.h index bfbcc7c308d..29c12ebb5c6 100644 --- a/mozilla/js/src/jsprvtd.h +++ b/mozilla/js/src/jsprvtd.h @@ -33,33 +33,67 @@ * make dependency induced by this file should not prove painful. */ +#include "jspubtd.h" + /* Scalar typedefs. */ typedef uint8 jsbytecode; typedef uint8 jssrcnote; -typedef int32 jsrefcount; typedef uint32 jsatomid; /* Struct typedefs. */ typedef struct JSCodeGenerator JSCodeGenerator; typedef struct JSGCThing JSGCThing; +typedef struct JSParseNode JSParseNode; +typedef struct JSSharpObjectMap JSSharpObjectMap; typedef struct JSToken JSToken; +typedef struct JSTokenPos JSTokenPos; +typedef struct JSTokenPtr JSTokenPtr; typedef struct JSTokenStream JSTokenStream; +typedef struct JSTreeContext JSTreeContext; +typedef struct JSTryNote JSTryNote; /* Friend "Advanced API" typedefs. */ typedef struct JSAtom JSAtom; typedef struct JSAtomList JSAtomList; +typedef struct JSAtomListElement JSAtomListElement; typedef struct JSAtomMap JSAtomMap; typedef struct JSAtomState JSAtomState; typedef struct JSCodeSpec JSCodeSpec; typedef struct JSPrinter JSPrinter; -typedef struct JSProperty JSProperty; typedef struct JSRegExp JSRegExp; typedef struct JSRegExpStatics JSRegExpStatics; typedef struct JSScope JSScope; typedef struct JSScopeOps JSScopeOps; +typedef struct JSScopeProperty JSScopeProperty; typedef struct JSStackFrame JSStackFrame; typedef struct JSSubString JSSubString; typedef struct JSSymbol JSSymbol; -typedef struct JSSharpObjectMap JSSharpObjectMap; + +/* "Friend" types used by jscntxt.h and jsdbgapi.h. */ +typedef enum JSTrapStatus { + JSTRAP_ERROR, + JSTRAP_CONTINUE, + JSTRAP_RETURN, + JSTRAP_LIMIT +} JSTrapStatus; + +typedef JSTrapStatus +(*JSTrapHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, + void *closure); + +/* called just after script creation */ +typedef void +(*JSNewScriptHook)( JSContext *cx, + const char *filename, /* URL this script loads from */ + uintN lineno, /* line where this script starts */ + JSScript *script, + JSFunction *fun, + void *callerdata ); + +/* called just before script destruction */ +typedef void +(*JSDestroyScriptHook)( JSContext *cx, + JSScript *script, + void *callerdata ); #endif /* jsprvtd_h___ */ diff --git a/mozilla/js/src/jspubtd.h b/mozilla/js/src/jspubtd.h index 09e3cbb4d46..1bc735806f6 100644 --- a/mozilla/js/src/jspubtd.h +++ b/mozilla/js/src/jspubtd.h @@ -22,11 +22,63 @@ * JS public API typedefs. */ #include "prtypes.h" - #define NETSCAPE_INTERNAL 1 #include "jscompat.h" -#define JS_FRIEND_API(t) PR_IMPLEMENT(t) +#ifndef PR_INLINE +# ifdef _WIN32 +# define PR_INLINE __inline +# elif defined(__GNUC__) +# define PR_INLINE inline +# else +# define PR_INLINE +# endif +#endif /* PR_INLINE */ + +/* + * Downrev NSPR versions of prtypes.h do not define these linkage-related + * PR_* macros, so we define them if necessary. Eventually, we should be + * able to remove these local definitions (move them to js/ref/prtypes.h, + * and get them from an uprev NSPR's prtypes.h for js/src). + */ +#ifndef PR_IMPORT_API +# ifdef _WIN32 +# define PR_IMPORT_API(__x) _declspec(dllimport) __x +# else +# define PR_IMPORT_API(__x) PR_IMPLEMENT(__x) +# endif +#endif /* PR_IMPORT_API */ +#ifndef PR_IMPORT_DATA +# ifdef _WIN32 +# define PR_IMPORT_DATA(__x) _declspec(dllimport) __x +# else +# define PR_IMPORT_DATA(__x) __x +# endif +#endif /* PR_IMPORT_DATA */ +#ifndef PR_PUBLIC_DATA +# ifdef _WIN32 +# define PR_PUBLIC_DATA(__x) _declspec(dllexport) __x +# else +# define PR_PUBLIC_DATA(__x) __x +# endif +#endif /* PR_PUBLIC_DATA */ + +/* + * The linkage of JS API functions differs depending on whether the file is + * used within the JS library or not. Any source file within the JS + * interpreter should define EXPORT_JS_API whereas any client of the library + * should not. + */ +#ifdef EXPORT_JS_API +#define JS_PUBLIC_API(t) PR_IMPLEMENT(t) +#define JS_PUBLIC_DATA(t) PR_PUBLIC_DATA(t) +#else +#define JS_PUBLIC_API(t) PR_IMPORT_API(t) +#define JS_PUBLIC_DATA(t) PR_IMPORT_DATA(t) +#endif + +#define JS_FRIEND_API(t) JS_PUBLIC_API(t) +#define JS_FRIEND_DATA(t) JS_PUBLIC_DATA(t) /* Scalar typedefs. */ typedef uint16 jschar; @@ -34,6 +86,8 @@ typedef int32 jsint; typedef uint32 jsuint; typedef float64 jsdouble; typedef prword jsval; +typedef prword jsid; +typedef prword jsrefcount; /* Boolean enum and packed int types. */ typedef PRBool JSBool; @@ -53,8 +107,10 @@ typedef enum JSVersion { #define JSVERSION_IS_ECMA(version) \ ((version) == JSVERSION_DEFAULT || (version) >= JSVERSION_1_3) +#define JSVERSION_IS_ECMAv2(version) \ + ((version) >= JSVERSION_1_3) -/* Typeof operator result enumeration. */ +/* Result of typeof operator enumeration. */ typedef enum JSType { JSTYPE_VOID, /* undefined */ JSTYPE_OBJECT, /* object */ @@ -65,6 +121,25 @@ typedef enum JSType { JSTYPE_LIMIT } JSType; +/* JSObjectOps.checkAccess mode enumeration. */ +typedef enum JSAccessMode { + JSACC_PROTO, + JSACC_PARENT, + JSACC_IMPORT, + JSACC_WATCH, + JSACC_LIMIT +} JSAccessMode; + +/* + * This enum type is used to control the behavior of a JSObject property + * iterator function that has type JSNewEnumerate. + */ +typedef enum JSIterateOp { + JSENUMERATE_INIT, /* Create new iterator state */ + JSENUMERATE_NEXT, /* Iterate once */ + JSENUMERATE_DESTROY /* Destroy iterator state */ +} JSIterateOp; + /* Struct typedefs. */ typedef struct JSClass JSClass; typedef struct JSConstDoubleSpec JSConstDoubleSpec; @@ -72,13 +147,17 @@ typedef struct JSContext JSContext; typedef struct JSErrorReport JSErrorReport; typedef struct JSFunction JSFunction; typedef struct JSFunctionSpec JSFunctionSpec; +typedef struct JSIdArray JSIdArray; +typedef struct JSProperty JSProperty; typedef struct JSPropertySpec JSPropertySpec; typedef struct JSObject JSObject; typedef struct JSObjectMap JSObjectMap; +typedef struct JSObjectOps JSObjectOps; typedef struct JSRuntime JSRuntime; typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ typedef struct JSScript JSScript; typedef struct JSString JSString; +typedef struct JSXDRState JSXDRState; #ifndef CRT_CALL #ifdef XP_OS2 @@ -88,10 +167,36 @@ typedef struct JSString JSString; #endif #endif -/* Typedefs for user-supplied functions called by the JS VM. */ +/* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */ + typedef JSBool (* CRT_CALL JSPropertyOp)(JSContext *cx, JSObject *obj, jsval id, jsval *vp); +/* + * This function type is used for callbacks that enumerate the properties of a + * 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 + * 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. + * JSENUMERATE_NEXT - A previously allocated opaque iterator state is passed + * in via statep. Return the next jsid in the iteration using *idp. + * The opaque iterator state pointed at by statep is destroyed and + * *statep is set to JSVAL_NULL if there are no properties left to + * enumerate. + * JSENUMERATE_DESTROY - Destroy the opaque iterator previous allocated by + * a call to this function with enum_op set to JSENUMERATE_INIT. + * + * The return value is always used to indicate success, with a value of JS_FALSE + * for failure. + */ +typedef JSBool +(* CRT_CALL JSNewEnumerateOp)(JSContext *cx, JSObject *obj, + JSIterateOp enum_op, + jsval *statep, jsid *idp); + typedef JSBool (* CRT_CALL JSEnumerateOp)(JSContext *cx, JSObject *obj); @@ -99,7 +204,7 @@ typedef JSBool (* CRT_CALL JSResolveOp)(JSContext *cx, JSObject *obj, jsval id); typedef JSBool -(* CRT_CALL JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, +(* CRT_CALL JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp); typedef JSBool @@ -108,10 +213,76 @@ typedef JSBool typedef void (* CRT_CALL JSFinalizeOp)(JSContext *cx, JSObject *obj); +typedef JSObjectOps * +(* CRT_CALL JSGetObjectOps)(JSContext *cx, JSClass *clasp); + +typedef JSBool +(* CRT_CALL JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsval id, + JSAccessMode mode, jsval *vp); + +typedef JSBool +(* CRT_CALL JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp); + +typedef JSBool +(* CRT_CALL JSHasInstanceOp)(JSContext *cx, JSObject *obj, jsval v, + JSBool *bp); + +/* JSObjectOps function pointer typedefs. */ + +typedef JSObjectMap * +(* CRT_CALL JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs, + JSObjectOps *ops, JSClass *clasp, + JSObject *obj); + +typedef void +(* CRT_CALL JSObjectMapOp)(JSContext *cx, JSObjectMap *map); + +typedef JSBool +(* CRT_CALL JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id, + JSObject **objp, JSProperty **propp +#if defined JS_THREADSAFE && defined DEBUG + , const char *file, uintN line +#endif + ); + +typedef JSBool +(* CRT_CALL JSDefinePropOp)(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs, JSProperty **propp); + +typedef JSBool +(* CRT_CALL JSPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +typedef JSBool +(* CRT_CALL JSAttributesOp)(JSContext *cx, JSObject *obj, jsid id, + JSProperty *prop, uintN *attrsp); + +typedef JSBool +(* CRT_CALL JSCheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id, + JSAccessMode mode, jsval *vp, uintN *attrsp); + +typedef JSObject * +(* CRT_CALL JSObjectOp)(JSContext *cx, JSObject *obj); + +typedef void +(* CRT_CALL JSPropertyRefOp)(JSContext *cx, JSObject *obj, JSProperty *prop); + +/* Typedef for native functions called by the JS VM. */ + typedef JSBool (* CRT_CALL JSNative)(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); +/* Callbacks and their arguments. */ + +typedef enum JSGCStatus { + JSGC_BEGIN, + JSGC_END +} JSGCStatus; + +typedef JSBool +(* CRT_CALL JSGCCallback)(JSContext *cx, JSGCStatus status); + typedef JSBool (* CRT_CALL JSBranchCallback)(JSContext *cx, JSScript *script); @@ -119,9 +290,4 @@ typedef void (* CRT_CALL JSErrorReporter)(JSContext *cx, const char *message, JSErrorReport *report); -#ifdef NETSCAPE_INTERNAL -/* XXX remove and provide transcoder API when JS uses Unicode */ -typedef int (* CRT_CALL JSCharScanner)(int, char); -#endif - #endif /* jspubtd_h___ */ diff --git a/mozilla/js/src/jsregexp.c b/mozilla/js/src/jsregexp.c index a4c4206f301..7d61825da8f 100644 --- a/mozilla/js/src/jsregexp.c +++ b/mozilla/js/src/jsregexp.c @@ -35,6 +35,7 @@ #include "jsconfig.h" #include "jsfun.h" #include "jsgc.h" +#include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" @@ -84,6 +85,7 @@ typedef enum REOp { REOP_UCFLATi = 34, /* case-independent REOP_UCFLAT */ REOP_UCFLAT1i = 35, /* case-independent REOP_UCFLAT1 */ REOP_ANCHOR1 = 36, /* first-char discriminating REOP_ANCHOR */ + REOP_NCCLASS = 37, /* negated 8-bit character class */ REOP_END } REOp; @@ -127,6 +129,7 @@ uint8 reopsize[] = { /* UCFLATi */ 2, /* (2 = op + len) + [len 2-byte chars] */ /* UCFLAT1i */ 3, /* op + hibyte + lobyte */ /* ANCHOR1 */ 1, + /* NCCLASS */ 1 + (CCLASS_CHARSET_SIZE / PR_BITS_PER_BYTE), /* END */ 0, }; @@ -234,6 +237,7 @@ static char *reopname[] = { "ucflati", "ucflat1i", "anchor1", + "ncclass", "end" }; @@ -572,6 +576,7 @@ ParseItem(CompilerState *state) { const jschar *cp; RENode *ren; + REOp op; cp = state->cp; switch (*cp) { @@ -595,14 +600,24 @@ ParseItem(CompilerState *state) case '\\': switch (*++cp) { case 'b': - state->cp = cp + 1; - return NewRENode(state, REOP_WBDRY, NULL); - + op = REOP_WBDRY; + break; case 'B': - state->cp = cp + 1; - return NewRENode(state, REOP_WNONBDRY, NULL); + op = REOP_WNONBDRY; + break; + default: + return ParseQuantAtom(state); } - /* FALL THROUGH */ + + /* + * Word boundaries and non-boundaries are flagged as non-empty so they + * will be prefixed by an anchoring node. + */ + state->cp = cp + 1; + ren = NewRENode(state, op, NULL); + if (ren) + ren->flags |= RENODE_NONEMPTY; + return ren; default:; } @@ -1394,7 +1409,7 @@ OptimizeRegExp(CompilerState *state, RENode *ren) * Coalesce adjacent FLAT and FLAT1 nodes. Also coalesce FLAT and * FLAT, which can result from deleting a coalesced FLAT1. */ - while ((next = ren->next) && + while ((next = ren->next) != NULL && !(next->flags & RENODE_ISJOIN) && (REOP(next) == REOP_FLAT || REOP(next) == REOP_FLAT1)) { if (REOP(next) == REOP_FLAT) { @@ -1663,8 +1678,8 @@ EmitRegExp(CompilerState *state, RENode *ren, JSRegExp *re) case REOP_UCCLASS: cp = ren->kid; if (*cp == '^') { - if (op == REOP_UCCLASS) - pc[0] = (jsbytecode)REOP_NUCCLASS; + pc[0] = (jsbytecode) + ((op == REOP_CCLASS) ? REOP_NCCLASS : REOP_NUCCLASS); fill = 0xff; cp++; } else { @@ -2254,12 +2269,16 @@ MatchRegExp(MatchState *state, jsbytecode *pc, const jschar *cp) #define NONDOT_SINGLE_CASES \ case REOP_CCLASS: \ - /* Split ops up into statements to keep MSVC1.52 from crashing. */\ + case REOP_NCCLASS: \ c = *cp; \ - byte = (uintN)c >> 3; \ - bit = c & 7; \ - bit = 1 << bit; \ - matched = pc[1 + byte] & bit; \ + if (c >= CCLASS_CHARSET_SIZE) { \ + matched = (op == REOP_NCCLASS); \ + } else { \ + byte = (uintN)c >> 3; \ + bit = c & 7; \ + bit = 1 << bit; \ + matched = pc[1 + byte] & bit; \ + } \ matchlen = 1; \ break; \ \ @@ -2537,10 +2556,10 @@ JSBool js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, JSBool test, jsval *rval) { - size_t i, length, start; MatchState state; jsbytecode *pc; const jschar *cp, *ep; + size_t i, length, start; void *mark; JSSubString *parsub, *morepar; JSBool ok; @@ -2549,7 +2568,6 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, uintN num, morenum; JSString *parstr, *matchstr; JSObject *obj; - JSProperty *prop; /* * Initialize a state struct to minimize recursive argument traffic. @@ -2618,7 +2636,7 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, * matches, an index property telling the length of the left context, * and an input property referring to the input string. */ - obj = JS_NewArrayObject(cx, 0, NULL); + obj = js_NewArrayObject(cx, 0, NULL); if (!obj) { ok = JS_FALSE; goto out; @@ -2626,12 +2644,12 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, *rval = OBJECT_TO_JSVAL(obj); #define DEFVAL(val, id) { \ - if (!js_DefineProperty(cx, obj, id, val, \ + ok = js_DefineProperty(cx, obj, id, val, \ JS_PropertyStub, JS_PropertyStub, \ - JSPROP_ENUMERATE)) { \ + JSPROP_ENUMERATE, NULL); \ + if (!ok) { \ cx->newborn[GCX_OBJECT] = NULL; \ cx->newborn[GCX_STRING] = NULL; \ - ok = JS_FALSE; \ goto out; \ } \ } @@ -2684,13 +2702,12 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, ok = JS_FALSE; goto out; } - prop = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1), - STRING_TO_JSVAL(parstr), NULL, NULL, - JSPROP_ENUMERATE); - if (!prop) { + ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1), + STRING_TO_JSVAL(parstr), NULL, NULL, + JSPROP_ENUMERATE, NULL); + if (!ok) { cx->newborn[GCX_OBJECT] = NULL; cx->newborn[GCX_STRING] = NULL; - ok = JS_FALSE; goto out; } } @@ -2704,9 +2721,9 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, * order (so they come after the elements). */ DEFVAL(INT_TO_JSVAL(start + state.skipped), - (jsval)cx->runtime->atomState.indexAtom); + (jsid)cx->runtime->atomState.indexAtom); DEFVAL(STRING_TO_JSVAL(str), - (jsval)cx->runtime->atomState.inputAtom); + (jsid)cx->runtime->atomState.inputAtom); } #undef DEFVAL @@ -2723,9 +2740,6 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, * perl4.036 "hi", "there" "hihitherehi therebye" * perl5 "hi", "hi there" "hihitherehi therebye" * js1.2 "hi", "there" "hihitheretherebye" - * - * Insofar as JS1.2 always defined $` as "left context from the last - * match" for global regexps, it was more consistent than perl4. */ res->leftContext.chars = str->chars + start; res->leftContext.length = state.skipped; @@ -2772,7 +2786,7 @@ regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); - JS_LOCK(cx); + JS_LOCK_OBJ(cx, obj); re = JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL); if (re) { switch (slot) { @@ -2790,7 +2804,7 @@ regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) break; } } - JS_UNLOCK(cx); + JS_UNLOCK_OBJ(cx, obj); return JS_TRUE; } @@ -2804,14 +2818,14 @@ regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); - JS_LOCK(cx); + JS_LOCK_OBJ(cx, obj); re = JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL); if (re && slot == REGEXP_LAST_INDEX) { if (!js_ValueToNumber(cx, *vp, &d)) return JS_FALSE; re->lastIndex = (size_t)d; } - JS_UNLOCK(cx); + JS_UNLOCK_OBJ(cx, obj); return JS_TRUE; } @@ -2838,8 +2852,7 @@ JSBool js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res) { JS_ClearRegExpStatics(cx); - return js_AddRoot(cx, &res->input) && - js_AddRoot(cx, &res->execWrapper); + return js_AddRoot(cx, &res->input, "res->input"); } void @@ -2850,7 +2863,6 @@ js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res) res->moreParens = NULL; } js_RemoveRoot(cx, &res->input); - js_RemoveRoot(cx, &res->execWrapper); } static JSBool @@ -2964,16 +2976,6 @@ static JSPropertySpec regexp_static_props[] = { {0} }; -static JSBool -regexp_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - if (type == JSTYPE_FUNCTION) { - cx->regExpStatics.execObject = obj; - *vp = OBJECT_TO_JSVAL(cx->regExpStatics.execWrapper); - } - return JS_TRUE; -} - static void regexp_finalize(JSContext *cx, JSObject *obj) { @@ -2985,11 +2987,67 @@ regexp_finalize(JSContext *cx, JSObject *obj) js_DestroyRegExp(cx, re); } +/* Forward static prototype. */ +static JSBool +regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return regexp_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); +} + +#if JS_HAS_XDR + +#include "jsxdrapi.h" + +static JSBool +regexp_xdrObject(JSXDRState *xdr, JSObject **objp) +{ + JSRegExp *re; + JSString *source; + uint8 flags; + + if (xdr->mode == JSXDR_ENCODE) { + re = JS_GetPrivate(xdr->cx, *objp); + if (!re) + return JS_FALSE; + source = re->source; + flags = re->flags; + } + if (!JS_XDRString(xdr, &source) || + !JS_XDRUint8(xdr, &flags)) { + return JS_FALSE; + } + if (xdr->mode == JSXDR_DECODE) { + *objp = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL); + if (!*objp) + return JS_FALSE; + re = js_NewRegExp(xdr->cx, source, flags); + if (!re) + return JS_FALSE; + if (!JS_SetPrivate(xdr->cx, *objp, re)) { + js_DestroyRegExp(xdr->cx, re); + return JS_FALSE; + } + } + return JS_TRUE; +} + +#else /* !JS_HAS_XDR */ + +#define regexp_xdrObject NULL + +#endif /* !JS_HAS_XDR */ + JSClass js_RegExpClass = { "RegExp", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, regexp_getProperty, regexp_setProperty, - JS_EnumerateStub, JS_ResolveStub, regexp_convert, regexp_finalize + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, regexp_finalize, + NULL, NULL, regexp_call, NULL, + regexp_xdrObject, }; static JSBool @@ -3003,19 +3061,25 @@ regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, uintN flags; JSString *str; - ok = JS_FALSE; - JS_LOCK(cx); - re = JS_GetInstancePrivate(cx, obj, &js_RegExpClass, argv); - if (!re) + if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) + return JS_FALSE; + ok = JS_TRUE; + JS_LOCK_OBJ(cx, obj); + re = JS_GetPrivate(cx, obj); + if (!re) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); goto out; + } length = re->source->length + 2; nflags = 0; for (flags = re->flags; flags != 0; flags &= flags - 1) nflags++; chars = JS_malloc(cx, (length + nflags + 1) * sizeof(jschar)); - if (!chars) + if (!chars) { + ok = JS_FALSE; goto out; + } chars[0] = '/'; js_strncpy(&chars[1], re->source->chars, length - 2); @@ -3031,12 +3095,12 @@ regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_NewString(cx, chars, length, 0); if (!str) { JS_free(cx, chars); + ok = JS_FALSE; goto out; } *rval = STRING_TO_JSVAL(str); - ok = JS_TRUE; out: - JS_UNLOCK(cx); + JS_UNLOCK_OBJ(cx, obj); return ok; } @@ -3044,43 +3108,48 @@ static JSBool regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSBool ok; - JSRegExp *re, *nre; JSString *opt, *str; + JSRegExp *oldre, *re; + JSBool ok; - ok = JS_FALSE; - JS_LOCK(cx); if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) - goto out; - re = JS_GetPrivate(cx, obj); + return JS_FALSE; opt = NULL; + JS_LOCK_OBJ(cx, obj); if (argc == 0) { str = cx->runtime->emptyString; } else { str = js_ValueToString(cx, argv[0]); - if (!str) + if (!str) { + ok = JS_FALSE; goto out; + } argv[0] = STRING_TO_JSVAL(str); if (argc > 1) { opt = js_ValueToString(cx, argv[1]); - if (!opt) + if (!opt) { + ok = JS_FALSE; goto out; + } argv[1] = STRING_TO_JSVAL(opt); } } - nre = js_NewRegExpOpt(cx, str, opt); - if (!nre) - goto out; - if (!JS_SetPrivate(cx, obj, nre)) { - js_DestroyRegExp(cx, nre); + re = js_NewRegExpOpt(cx, str, opt); + if (!re) { + ok = JS_FALSE; goto out; } - if (re) + oldre = JS_GetPrivate(cx, obj); + ok = JS_SetPrivate(cx, obj, re); + if (!ok) { js_DestroyRegExp(cx, re); + goto out; + } + if (oldre) + js_DestroyRegExp(cx, oldre); *rval = OBJECT_TO_JSVAL(obj); - ok = JS_TRUE; out: - JS_UNLOCK(cx); + JS_UNLOCK_OBJ(cx, obj); return ok; } @@ -3093,10 +3162,12 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSString *str; size_t i; - ok = locked = JS_FALSE; - re = JS_GetInstancePrivate(cx, obj, &js_RegExpClass, argv); + if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) + return JS_FALSE; + re = JS_GetPrivate(cx, obj); if (!re) - goto out; + return JS_TRUE; + ok = locked = JS_FALSE; if (argc == 0) { str = cx->regExpStatics.input; if (!str) { @@ -3113,7 +3184,7 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, argv[0] = STRING_TO_JSVAL(str); } if (re->flags & JSREG_GLOB) { - JS_LOCK(cx); + JS_LOCK_OBJ(cx, obj); locked = JS_TRUE; i = re->lastIndex; } else { @@ -3124,7 +3195,7 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, re->lastIndex = (*rval == JSVAL_NULL) ? 0 : i; out: if (locked) - JS_UNLOCK(cx); + JS_UNLOCK_OBJ(cx, obj); return ok; } @@ -3144,18 +3215,10 @@ regexp_test(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_TRUE; } -static JSBool -regexp_execWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSBool ok; - - ok = regexp_exec(cx, cx->regExpStatics.execObject, argc, argv, rval); - cx->regExpStatics.execObject = NULL; - return ok; -} - static JSFunctionSpec regexp_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, regexp_toString, 0}, +#endif {js_toString_str, regexp_toString, 0}, {"compile", regexp_compile, 1}, {"exec", regexp_exec, 0}, @@ -3163,18 +3226,24 @@ static JSFunctionSpec regexp_methods[] = { {0} }; +static JSBool +RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* If not constructing, replace obj with a new RegExp object. */ + if (!cx->fp->constructing) { + obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); + if (!obj) + return JS_FALSE; + } + return regexp_compile(cx, obj, argc, argv, rval); +} + JSObject * js_InitRegExpClass(JSContext *cx, JSObject *obj) { - JSFunction *fun; JSObject *proto, *ctor; - fun = JS_NewFunction(cx, regexp_execWrapper, 0, 0, NULL, "execWrapper"); - if (!fun) - return NULL; - cx->regExpStatics.execWrapper = fun->object; - - proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, regexp_compile, 1, + proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1, regexp_props, regexp_methods, regexp_static_props, NULL); diff --git a/mozilla/js/src/jsregexp.h b/mozilla/js/src/jsregexp.h index 3e20e0b5b7b..ad380b028d2 100644 --- a/mozilla/js/src/jsregexp.h +++ b/mozilla/js/src/jsregexp.h @@ -36,8 +36,6 @@ struct JSRegExpStatics { JSSubString lastParen; /* last paren matched (perl $+) */ JSSubString leftContext; /* input to left of last match (perl $`) */ JSSubString rightContext; /* input to right of last match (perl $') */ - JSObject *execWrapper; /* exec() wrapper function (GC root) */ - JSObject *execObject; /* weak link to this param of /x+/("xyz") */ }; /* @@ -88,9 +86,9 @@ js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res); extern void js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res); -#define JSVAL_IS_REGEXP(v) \ +#define JSVAL_IS_REGEXP(cx, v) \ (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) && \ - JSVAL_TO_OBJECT(v)->map->clasp == &js_RegExpClass) + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_RegExpClass) extern JSClass js_RegExpClass; @@ -103,4 +101,7 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj); extern JSObject * js_NewRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags); +extern JSBool +js_XDRRegExp(JSXDRState *xdr, JSObject **objp); + #endif /* jsregexp_h___ */ diff --git a/mozilla/js/src/jsscan.c b/mozilla/js/src/jsscan.c index 48925623ef8..d1307de9d89 100644 --- a/mozilla/js/src/jsscan.c +++ b/mozilla/js/src/jsscan.c @@ -41,7 +41,6 @@ #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" -#include "jsemit.h" #include "jsnum.h" #include "jsopcode.h" #include "jsregexp.h" @@ -59,44 +58,59 @@ static struct keyword { {"case", TOK_CASE, JSOP_NOP}, {"continue", TOK_CONTINUE, JSOP_NOP}, {"default", TOK_DEFAULT, JSOP_NOP}, - {"delete", TOK_DELETE, JSOP_NOP}, + {js_delete_str, TOK_DELETE, JSOP_NOP}, {"do", TOK_DO, JSOP_NOP}, {"else", TOK_ELSE, JSOP_NOP}, {"export", TOK_EXPORT, JSOP_NOP, JSVERSION_1_2}, - {"false", TOK_PRIMARY, JSOP_FALSE}, + {js_false_str, TOK_PRIMARY, JSOP_FALSE}, {"for", TOK_FOR, JSOP_NOP}, {"function", TOK_FUNCTION, JSOP_NOP}, {"if", TOK_IF, JSOP_NOP}, - {"in", TOK_IN, JSOP_NOP}, - {"new", TOK_NEW, JSOP_NEW}, - {"null", TOK_PRIMARY, JSOP_NULL}, + {js_in_str, TOK_IN, JSOP_IN}, + {js_new_str, TOK_NEW, JSOP_NEW}, + {js_null_str, TOK_PRIMARY, JSOP_NULL}, {"return", TOK_RETURN, JSOP_NOP}, {"switch", TOK_SWITCH, JSOP_NOP}, - {"this", TOK_PRIMARY, JSOP_THIS}, - {"true", TOK_PRIMARY, JSOP_TRUE}, - {"typeof", TOK_UNARYOP, JSOP_TYPEOF}, + {js_this_str, TOK_PRIMARY, JSOP_THIS}, + {js_true_str, TOK_PRIMARY, JSOP_TRUE}, + {js_typeof_str, TOK_UNARYOP, JSOP_TYPEOF}, {"var", TOK_VAR, JSOP_NOP}, - {"void", TOK_UNARYOP, JSOP_VOID}, + {js_void_str, TOK_UNARYOP, JSOP_VOID}, {"while", TOK_WHILE, JSOP_NOP}, {"with", TOK_WITH, JSOP_NOP}, +#if JS_HAS_EXCEPTIONS + {"try", TOK_TRY, JSOP_NOP}, + {"catch", TOK_CATCH, JSOP_NOP}, + {"finally", TOK_FINALLY, JSOP_NOP}, + {"throw", TOK_THROW, JSOP_NOP}, +#else + {"try", TOK_RESERVED, JSOP_NOP}, + {"catch", TOK_RESERVED, JSOP_NOP}, + {"finally", TOK_RESERVED, JSOP_NOP}, + {"throw", TOK_RESERVED, JSOP_NOP}, +#endif + +#if JS_HAS_INSTANCEOF + {js_instanceof_str, TOK_INSTANCEOF, JSOP_INSTANCEOF}, +#else + {js_instanceof_str, TOK_RESERVED, JSOP_NOP}, +#endif + #ifdef RESERVE_JAVA_KEYWORDS {"abstract", TOK_RESERVED, JSOP_NOP}, {"boolean", TOK_RESERVED, JSOP_NOP}, {"byte", TOK_RESERVED, JSOP_NOP}, - {"catch", TOK_RESERVED, JSOP_NOP}, {"char", TOK_RESERVED, JSOP_NOP}, {"class", TOK_RESERVED, JSOP_NOP}, {"const", TOK_RESERVED, JSOP_NOP}, {"double", TOK_RESERVED, JSOP_NOP}, {"extends", TOK_RESERVED, JSOP_NOP}, {"final", TOK_RESERVED, JSOP_NOP}, - {"finally", TOK_RESERVED, JSOP_NOP}, {"float", TOK_RESERVED, JSOP_NOP}, {"goto", TOK_RESERVED, JSOP_NOP}, {"implements", TOK_RESERVED, JSOP_NOP}, {"import", TOK_IMPORT, JSOP_NOP}, - {"instanceof", TOK_RESERVED, JSOP_NOP}, {"int", TOK_RESERVED, JSOP_NOP}, {"interface", TOK_RESERVED, JSOP_NOP}, {"long", TOK_RESERVED, JSOP_NOP}, @@ -109,10 +123,8 @@ static struct keyword { {"static", TOK_RESERVED, JSOP_NOP}, {"super", TOK_PRIMARY, JSOP_NOP}, {"synchronized", TOK_RESERVED, JSOP_NOP}, - {"throw", TOK_RESERVED, JSOP_NOP}, {"throws", TOK_RESERVED, JSOP_NOP}, {"transient", TOK_RESERVED, JSOP_NOP}, - {"try", TOK_RESERVED, JSOP_NOP}, {"volatile", TOK_RESERVED, JSOP_NOP}, #endif @@ -126,7 +138,7 @@ js_InitScanner(JSContext *cx) JSAtom *atom; for (kw = keywords; kw->name; kw++) { - atom = js_Atomize(cx, kw->name, strlen(kw->name), 0); + atom = js_Atomize(cx, kw->name, strlen(kw->name), ATOM_PINNED); if (!atom) return JS_FALSE; atom->kwindex = (kw->version <= cx->version) ? kw - keywords : -1; @@ -180,29 +192,41 @@ js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length) ts->userbuf.base = (jschar *)base; ts->userbuf.limit = (jschar *)base + length; ts->userbuf.ptr = (jschar *)base; +#ifdef JSD_LOWLEVEL_SOURCE + ts->jsdc = JSD_JSDContextForJSContext(cx); +#endif return ts; } #ifdef JSFILE JS_FRIEND_API(JSTokenStream *) -js_NewFileTokenStream(JSContext *cx, const char *filename) +js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp) { + jschar *base; JSTokenStream *ts; FILE *file; - ts = js_NewBufferTokenStream(cx, NULL, 0); + PR_ARENA_ALLOCATE(base, &cx->tempPool, JS_LINE_LIMIT * sizeof(jschar)); + if (!base) + return NULL; + ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT); if (!ts) return NULL; - file = fopen(filename, "r"); - if (!file) { - JS_ReportError(cx, "can't open %s: %s", filename, strerror(errno)); - return NULL; + if (!filename || strcmp(filename, "-") == 0) { + file = defaultfp; + } else { + file = fopen(filename, "r"); + if (!file) { + JS_ReportError(cx, "can't open %s: %s", filename, strerror(errno)); + return NULL; + } } + ts->userbuf.ptr = ts->userbuf.limit; ts->file = file; ts->filename = filename; return ts; } -#endif +#endif /* JSFILE */ JS_FRIEND_API(JSBool) js_CloseTokenStream(JSContext *cx, JSTokenStream *ts) @@ -216,83 +240,166 @@ js_CloseTokenStream(JSContext *cx, JSTokenStream *ts) #endif } +#ifdef JSD_LOWLEVEL_SOURCE +static void +SendSourceToJSDebugger(JSTokenStream *ts, jschar *str, size_t length) +{ + if (!ts->jsdsrc) { + ts->jsdsrc = JSD_NewSourceText(ts->jsdc, + ts->filename ? ts->filename : "typein"); + } + if (ts->jsdsrc) { + /* here we convert our Unicode into a C string to pass to JSD */ +#define JSD_BUF_SIZE 1024 + static char* buf = NULL; + int remaining = length; + + if (!buf) + buf = malloc(JSD_BUF_SIZE); + if (buf) + { + while (remaining && ts->jsdsrc) { + int bytes = PR_MIN(remaining, JSD_BUF_SIZE); + int i; + for (i = 0; i < bytes; i++) + buf[i] = (const char) *(str++); + ts->jsdsrc = JSD_AppendSourceText(ts->jsdc,ts->jsdsrc, + buf, bytes, + JSD_SOURCE_PARTIAL); + remaining -= bytes; + } + } + } +} +#endif + static int32 GetChar(JSTokenStream *ts) { - ptrdiff_t length; - jschar *nl; int32 c; + ptrdiff_t len, olen; + jschar *nl; if (ts->ungetpos != 0) { c = ts->ungetbuf[--ts->ungetpos]; } else { - if (ts->linebuf.ptr >= ts->linebuf.limit) { + if (ts->linebuf.ptr == ts->linebuf.limit) { + len = ts->userbuf.limit - ts->userbuf.ptr; + if (len <= 0) { #ifdef JSFILE - if (ts->file) { - char cbuf[JS_LINE_LIMIT]; - ptrdiff_t i; + /* Fill ts->userbuf so that \r and \r\n convert to \n. */ + if (ts->file) { + JSBool crflag; + char cbuf[JS_LINE_LIMIT]; + jschar *ubuf; + ptrdiff_t i, j; - if (!fgets(cbuf, JS_LINE_LIMIT, ts->file)) { - ts->flags |= TSF_EOF; - return EOF; - } - length = strlen(cbuf); - for (i = 0; i < length; i++) - ts->linebuf.base[i] = (jschar) cbuf[i]; - } else -#endif - { - length = ts->userbuf.limit - ts->userbuf.ptr; - if (length <= 0) { - ts->flags |= TSF_EOF; - return EOF; - } - - /* - * Any one of \n, \r, or \r\n ends a line (longest match wins). - */ - for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) { - if (*nl == '\n') - break; - if (*nl == '\r') { - if (nl + 1 < ts->userbuf.limit && nl[1] == '\n') - nl++; - break; + crflag = (ts->flags & TSF_CRFLAG) != 0; + if (!fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file)) { + ts->flags |= TSF_EOF; + return EOF; } - } - - /* - * If there was a line terminator, copy thru it into linebuf. - * Else copy JS_LINE_LIMIT-1 bytes into linebuf. - */ - if (nl < ts->userbuf.limit) - length = nl - ts->userbuf.ptr + 1; - if (length >= JS_LINE_LIMIT) - length = JS_LINE_LIMIT - 1; - js_strncpy(ts->linebuf.base, ts->userbuf.ptr, length); - ts->userbuf.ptr += length; - - /* - * Make sure linebuf contains \n for EOL (don't do this in - * userbuf because the user's string might be readonly). - */ - if (nl < ts->userbuf.limit) { - if (*nl == '\r') { - if (ts->linebuf.base[length-1] == '\r') - ts->linebuf.base[length-1] = '\n'; - } else if (*nl == '\n') { - if (nl > ts->userbuf.base && - nl[-1] == '\r' && - ts->linebuf.base[length-2] == '\r') { - length--; - PR_ASSERT(ts->linebuf.base[length] == '\n'); - ts->linebuf.base[length-1] = '\n'; + len = olen = strlen(cbuf); + PR_ASSERT(len > 0); + ubuf = ts->userbuf.base; + i = 0; + if (crflag) { + ts->flags &= ~TSF_CRFLAG; + if (cbuf[0] != '\n') { + ubuf[i++] = '\n'; + len++; + ts->linepos--; } } + for (j = 0; i < len; i++, j++) + ubuf[i] = (jschar) cbuf[j]; + ts->userbuf.limit = ubuf + len; + ts->userbuf.ptr = ubuf; + } else +#endif /* JSFILE */ + { + ts->flags |= TSF_EOF; + return EOF; + } + } + +#ifdef JSD_LOWLEVEL_SOURCE + if (ts->jsdc) + SendSourceToJSDebugger(ts, ts->userbuf.ptr, len); +#endif + + /* + * Any one of \n, \r, or \r\n ends a line (longest match wins). + */ + for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) { + if (*nl == '\n') + break; + if (*nl == '\r') { + if (nl + 1 < ts->userbuf.limit && nl[1] == '\n') + nl++; + break; + } + } + + /* + * If there was a line terminator, copy thru it into linebuf. + * Else copy JS_LINE_LIMIT-1 bytes into linebuf. + */ + if (nl < ts->userbuf.limit) + len = nl - ts->userbuf.ptr + 1; + if (len >= JS_LINE_LIMIT) + len = JS_LINE_LIMIT - 1; + js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len); + ts->userbuf.ptr += len; + olen = len; + + /* + * Make sure linebuf contains \n for EOL (don't do this in + * userbuf because the user's string might be readonly). + */ + if (nl < ts->userbuf.limit) { + if (*nl == '\r') { + if (ts->linebuf.base[len-1] == '\r') { +#ifdef JSFILE + /* + * Does the line segment end in \r? We must check + * for a \n at the front of the next segment before + * storing a \n into linebuf. + */ + if (nl + 1 == ts->userbuf.limit) { + len--; + ts->flags |= TSF_CRFLAG; + } else +#endif + ts->linebuf.base[len-1] = '\n'; + } + } else if (*nl == '\n') { + if (nl > ts->userbuf.base && + nl[-1] == '\r' && + ts->linebuf.base[len-2] == '\r') { + len--; + PR_ASSERT(ts->linebuf.base[len] == '\n'); + ts->linebuf.base[len-1] = '\n'; + } } } - ts->linebuf.limit = ts->linebuf.base + length; + + /* Reset linebuf based on adjusted segment length. */ + ts->linebuf.limit = ts->linebuf.base + len; ts->linebuf.ptr = ts->linebuf.base; + + /* Update position of linebuf within physical line in userbuf. */ + if (!(ts->flags & TSF_NLFLAG)) + ts->linepos += ts->linelen; + else + ts->linepos = 0; + if (ts->linebuf.limit[-1] == '\n') + ts->flags |= TSF_NLFLAG; + else + ts->flags &= ~TSF_NLFLAG; + + /* Update linelen from original segment length. */ + ts->linelen = olen; } c = *ts->linebuf.ptr++; } @@ -413,22 +520,20 @@ js_ReportCompileError(JSContext *cx, JSTokenStream *ts, const char *format, } JSTokenType -js_PeekToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +js_PeekToken(JSContext *cx, JSTokenStream *ts) { JSTokenType tt; tt = ts->pushback.type; if (tt == TOK_EOF) { - ts->flags |= TSF_LOOKAHEAD; - tt = js_GetToken(cx, ts, cg); - ts->flags &= ~TSF_LOOKAHEAD; + tt = js_GetToken(cx, ts); js_UngetToken(ts); } return tt; } JSTokenType -js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts) { uintN newlines; JSTokenType tt; @@ -436,7 +541,7 @@ js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) newlines = ts->flags & TSF_NEWLINES; if (!newlines) SCAN_NEWLINES(ts); - tt = js_PeekToken(cx, ts, cg); + tt = js_PeekToken(cx, ts); if (!newlines) HIDE_NEWLINES(ts); return tt; @@ -483,7 +588,7 @@ AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c) } JSTokenType -js_GetToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) +js_GetToken(JSContext *cx, JSTokenStream *ts) { int32 c; JSAtom *atom; @@ -492,31 +597,15 @@ js_GetToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) #define FINISH_TOKENBUF(tb) if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR) #define TOKEN_LENGTH(tb) ((tb)->ptr - (tb)->base - 1) #define RETURN(tt) { if (tt == TOK_ERROR) ts->flags |= TSF_ERROR; \ + ts->token.pos.end.index = ts->linepos + \ + (ts->linebuf.ptr - ts->linebuf.base) - \ + ts->ungetpos; \ return (ts->token.type = tt); } -#define NOTE_NEWLINE() { \ - if (cg) { \ - if (ts->flags & TSF_LOOKAHEAD) { \ - /* Don't emit newline source notes during lookahead. */ \ - ts->newlines++; \ - } else { \ - if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0) \ - RETURN(TOK_ERROR); \ - } \ - } \ -} - /* If there was a fatal error, keep returning TOK_ERROR. */ if (ts->flags & TSF_ERROR) return TOK_ERROR; - /* If done with lookahead, emit source notes for any skipped newlines. */ - if (!(ts->flags & TSF_LOOKAHEAD) && - ts->newlines != 0 && - !js_FlushNewlines(cx, ts, cg)) { - RETURN(TOK_ERROR); - } - /* Check for a pushed-back token resulting from mismatching lookahead. */ if (ts->pushback.type != TOK_EOF) { ts->token = ts->pushback; @@ -527,14 +616,14 @@ js_GetToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) retry: do { c = GetChar(ts); - if (c == '\n') { - NOTE_NEWLINE(); - if (ts->flags & TSF_NEWLINES) - break; - } + if (c == '\n' && (ts->flags & TSF_NEWLINES)) + break; } while (JS_ISSPACE(c)); ts->token.ptr = ts->linebuf.ptr - 1; + ts->token.pos.begin.index = ts->linepos + (ts->token.ptr-ts->linebuf.base); + ts->token.pos.begin.lineno = ts->token.pos.end.lineno = ts->lineno; + if (c == EOF) RETURN(TOK_EOF); @@ -551,17 +640,18 @@ retry: atom = js_AtomizeChars(cx, ts->tokenbuf.base, TOKEN_LENGTH(&ts->tokenbuf), - ATOM_NOHOLD); + 0); if (!atom) RETURN(TOK_ERROR); if (atom->kwindex >= 0) { struct keyword *kw; kw = &keywords[atom->kwindex]; - ts->token.u.op = kw->op; + ts->token.t_op = kw->op; RETURN(kw->tokentype); } - ts->token.u.atom = atom; + ts->token.t_op = JSOP_NAME; + ts->token.t_atom = atom; RETURN(TOK_NAME); } @@ -641,7 +731,7 @@ retry: RETURN(TOK_ERROR); } } - ts->token.u.dval = dval; + ts->token.t_dval = dval; RETURN(TOK_NUMBER); } @@ -700,8 +790,6 @@ retry: c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); SkipChars(ts, 2); } - } else if (c == '\n') { - NOTE_NEWLINE(); } break; } @@ -713,16 +801,19 @@ retry: atom = js_AtomizeChars(cx, ts->tokenbuf.base, TOKEN_LENGTH(&ts->tokenbuf), - ATOM_NOHOLD); + 0); if (!atom) RETURN(TOK_ERROR); - ts->token.u.atom = atom; + ts->token.pos.end.lineno = ts->lineno; + ts->token.t_op = JSOP_STRING; + ts->token.t_atom = atom; RETURN(TOK_STRING); } switch (c) { case '\n': c = TOK_EOL; break; case ';': c = TOK_SEMI; break; + case '.': c = TOK_DOT; break; case '[': c = TOK_LB; break; case ']': c = TOK_RB; break; case '{': c = TOK_LC; break; @@ -732,13 +823,12 @@ retry: case ',': c = TOK_COMMA; break; case '?': c = TOK_HOOK; break; case ':': c = TOK_COLON; break; - case '.': c = TOK_DOT; break; case '|': if (MatchChar(ts, c)) { c = TOK_OR; } else if (MatchChar(ts, '=')) { - ts->token.u.op = JSOP_BITOR; + ts->token.t_op = JSOP_BITOR; c = TOK_ASSIGN; } else { c = TOK_BITOR; @@ -747,7 +837,7 @@ retry: case '^': if (MatchChar(ts, '=')) { - ts->token.u.op = JSOP_BITXOR; + ts->token.t_op = JSOP_BITXOR; c = TOK_ASSIGN; } else { c = TOK_BITXOR; @@ -758,7 +848,7 @@ retry: if (MatchChar(ts, c)) { c = TOK_AND; } else if (MatchChar(ts, '=')) { - ts->token.u.op = JSOP_BITAND; + ts->token.t_op = JSOP_BITAND; c = TOK_ASSIGN; } else { c = TOK_BITAND; @@ -768,13 +858,13 @@ retry: case '=': if (MatchChar(ts, c)) { #if JS_HAS_TRIPLE_EQOPS - ts->token.u.op = MatchChar(ts, c) ? JSOP_NEW_EQ : cx->jsop_eq; + ts->token.t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : cx->jsop_eq; #else - ts->token.u.op = cx->jsop_eq; + ts->token.t_op = cx->jsop_eq; #endif c = TOK_EQOP; } else { - ts->token.u.op = JSOP_NOP; + ts->token.t_op = JSOP_NOP; c = TOK_ASSIGN; } break; @@ -782,13 +872,13 @@ retry: case '!': if (MatchChar(ts, '=')) { #if JS_HAS_TRIPLE_EQOPS - ts->token.u.op = MatchChar(ts, '=') ? JSOP_NEW_NE : cx->jsop_ne; + ts->token.t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : cx->jsop_ne; #else - ts->token.u.op = cx->jsop_ne; + ts->token.t_op = cx->jsop_ne; #endif c = TOK_EQOP; } else { - ts->token.u.op = JSOP_NOT; + ts->token.t_op = JSOP_NOT; c = TOK_UNARYOP; } break; @@ -804,26 +894,26 @@ retry: UngetChar(ts, '!'); } if (MatchChar(ts, c)) { - ts->token.u.op = JSOP_LSH; + ts->token.t_op = JSOP_LSH; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP; } else { - ts->token.u.op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT; + ts->token.t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT; c = TOK_RELOP; } break; case '>': if (MatchChar(ts, c)) { - ts->token.u.op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH; + ts->token.t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP; } else { - ts->token.u.op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT; + ts->token.t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT; c = TOK_RELOP; } break; case '*': - ts->token.u.op = JSOP_MUL; + ts->token.t_op = JSOP_MUL; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR; break; @@ -836,11 +926,9 @@ skipline: goto retry; } if (MatchChar(ts, '*')) { - while ((c = GetChar(ts)) != EOF - && !(c == '*' && MatchChar(ts, '/'))) { - if (c == '\n') { - NOTE_NEWLINE(); - } else if (c == '/' && MatchChar(ts, '*')) { + while ((c = GetChar(ts)) != EOF && + !(c == '*' && MatchChar(ts, '/'))) { + if (c == '/' && MatchChar(ts, '*')) { if (MatchChar(ts, '/')) goto retry; js_ReportCompileError(cx, ts, "nested comment"); @@ -888,6 +976,7 @@ skipline: ts->token.ptr = ts->linebuf.ptr - 1; js_ReportCompileError(cx, ts, "invalid flag after regular expression"); + (void) GetChar(ts); RETURN(TOK_ERROR); } obj = js_NewRegExpObject(cx, @@ -896,40 +985,41 @@ skipline: flags); if (!obj) RETURN(TOK_ERROR); - atom = js_AtomizeObject(cx, obj, ATOM_NOHOLD); + atom = js_AtomizeObject(cx, obj, 0); if (!atom) RETURN(TOK_ERROR); - ts->token.u.atom = atom; + ts->token.t_op = JSOP_OBJECT; + ts->token.t_atom = atom; RETURN(TOK_OBJECT); } #endif /* JS_HAS_REGEXPS */ - ts->token.u.op = JSOP_DIV; + ts->token.t_op = JSOP_DIV; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP; break; case '%': - ts->token.u.op = JSOP_MOD; + ts->token.t_op = JSOP_MOD; c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP; break; case '~': - ts->token.u.op = JSOP_BITNOT; + ts->token.t_op = JSOP_BITNOT; c = TOK_UNARYOP; break; case '+': case '-': if (MatchChar(ts, '=')) { - ts->token.u.op = (c == '+') ? JSOP_ADD : JSOP_SUB; + ts->token.t_op = (c == '+') ? JSOP_ADD : JSOP_SUB; c = TOK_ASSIGN; } else if (MatchChar(ts, c)) { c = (c == '+') ? TOK_INC : TOK_DEC; } else if (c == '-') { - ts->token.u.op = JSOP_NEG; + ts->token.t_op = JSOP_NEG; c = TOK_MINUS; } else { - ts->token.u.op = JSOP_POS; + ts->token.t_op = JSOP_POS; c = TOK_PLUS; } break; @@ -956,7 +1046,7 @@ skipline: RETURN(TOK_ERROR); } } - ts->token.u.dval = (jsdouble) n; + ts->token.t_dval = (jsdouble) n; if (c == '=') RETURN(TOK_DEFSHARP); if (c == '#') @@ -964,9 +1054,9 @@ skipline: goto badchar; } + badchar: #endif /* JS_HAS_SHARP_VARS */ - badchar: default: js_ReportCompileError(cx, ts, "illegal character"); RETURN(TOK_ERROR); @@ -979,7 +1069,6 @@ skipline: #undef FINISH_TOKENBUF #undef TOKEN_LENGTH #undef RETURN -#undef NOTE_NEWLINE } void @@ -992,30 +1081,10 @@ js_UngetToken(JSTokenStream *ts) } JSBool -js_MatchToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, - JSTokenType tt) +js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt) { - JSTokenType got; - - ts->flags |= TSF_LOOKAHEAD; - got = js_GetToken(cx, ts, cg); - ts->flags &= ~TSF_LOOKAHEAD; - if (got == tt) { - if (ts->newlines != 0 && !js_FlushNewlines(cx, ts, cg)) - return JS_FALSE; + if (js_GetToken(cx, ts) == tt) return JS_TRUE; - } js_UngetToken(ts); return JS_FALSE; } - -JSBool -js_FlushNewlines(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg) -{ - while (ts->newlines != 0) { - if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0) - return JS_FALSE; - ts->newlines--; - } - return JS_TRUE; -} diff --git a/mozilla/js/src/jsscan.h b/mozilla/js/src/jsscan.h index 5105bca93b0..2695b687f7b 100644 --- a/mozilla/js/src/jsscan.h +++ b/mozilla/js/src/jsscan.h @@ -29,73 +29,102 @@ #include "jsprvtd.h" #include "jspubtd.h" +#ifdef JSD_LOWLEVEL_SOURCE +#include "jsdebug.h" +#endif + PR_BEGIN_EXTERN_C typedef enum JSTokenType { TOK_ERROR = -1, /* well-known as the only code < EOF */ - TOK_EOF, /* end of file */ - TOK_EOL, /* end of line */ - TOK_SEMI, /* semicolon */ - TOK_LB, TOK_RB, /* left and right brackets */ - TOK_LC, TOK_RC, /* left and right curlies (braces) */ - TOK_LP, TOK_RP, /* left and right parentheses */ - TOK_COMMA, /* comma operator */ - TOK_ASSIGN, /* assignment ops (= += -= etc.) */ - TOK_HOOK, TOK_COLON, /* conditional (?:) */ - TOK_OR, /* logical or (||) */ - TOK_AND, /* logical and (&&) */ - TOK_BITOR, /* bitwise-or (|) */ - TOK_BITXOR, /* bitwise-xor (^) */ - TOK_BITAND, /* bitwise-and (&) */ - TOK_EQOP, /* equality ops (== !=) */ - TOK_RELOP, /* relational ops (< <= > >=) */ - TOK_SHOP, /* shift ops (<< >> >>>) */ - TOK_PLUS, /* plus */ - TOK_MINUS, /* minus */ - TOK_STAR, TOK_DIVOP, /* multiply/divide ops (* / %) */ - TOK_UNARYOP, /* unary prefix operator */ - TOK_INC, TOK_DEC, /* increment/decrement (++ --) */ - TOK_DOT, /* member operator (.) */ - TOK_NAME, /* identifier */ - TOK_NUMBER, /* numeric constant */ - TOK_STRING, /* string constant */ - TOK_OBJECT, /* RegExp or other object constant */ - TOK_PRIMARY, /* true, false, null, this, super */ - TOK_FUNCTION, /* function keyword */ - TOK_EXPORT, TOK_IMPORT, /* export keyword */ - TOK_IF, /* if keyword */ - TOK_ELSE, /* else keyword */ - TOK_SWITCH, /* switch keyword */ - TOK_CASE, /* case keyword */ - TOK_DEFAULT, /* default keyword */ - TOK_WHILE, /* while keyword */ - TOK_DO, /* do keyword */ - TOK_FOR, /* for keyword */ - TOK_BREAK, /* break keyword */ - TOK_CONTINUE, /* continue keyword */ - TOK_IN, /* in keyword */ - TOK_VAR, /* var keyword */ - TOK_WITH, /* with keyword */ - TOK_RETURN, /* return keyword */ - TOK_NEW, /* new keyword */ - TOK_DELETE, /* delete keyword */ - TOK_DEFSHARP, TOK_USESHARP, /* #n=, #n# for object/array literals */ + TOK_EOF = 0, /* end of file */ + TOK_EOL = 1, /* end of line */ + TOK_SEMI = 2, /* semicolon */ + TOK_LB = 3, TOK_RB = 4, /* left and right brackets */ + TOK_LC = 5, TOK_RC = 6, /* left and right curlies (braces) */ + TOK_LP = 7, TOK_RP = 8, /* left and right parentheses */ + TOK_COMMA = 9, /* comma operator */ + TOK_ASSIGN = 10, /* assignment ops (= += -= etc.) */ + TOK_HOOK = 11, TOK_COLON = 12, /* conditional (?:) */ + TOK_OR = 13, /* logical or (||) */ + TOK_AND = 14, /* logical and (&&) */ + TOK_BITOR = 15, /* bitwise-or (|) */ + TOK_BITXOR = 16, /* bitwise-xor (^) */ + TOK_BITAND = 17, /* bitwise-and (&) */ + TOK_EQOP = 18, /* equality ops (== !=) */ + TOK_RELOP = 19, /* relational ops (< <= > >=) */ + TOK_SHOP = 20, /* shift ops (<< >> >>>) */ + TOK_PLUS = 21, /* plus */ + TOK_MINUS = 22, /* minus */ + TOK_STAR = 23, TOK_DIVOP = 24, /* multiply/divide ops (* / %) */ + TOK_UNARYOP = 25, /* unary prefix operator */ + TOK_INC = 26, TOK_DEC = 27, /* increment/decrement (++ --) */ + TOK_DOT = 28, /* member operator (.) */ + TOK_NAME = 29, /* identifier */ + TOK_NUMBER = 30, /* numeric constant */ + TOK_STRING = 31, /* string constant */ + TOK_OBJECT = 32, /* RegExp or other object constant */ + TOK_PRIMARY = 33, /* true, false, null, this, super */ + TOK_FUNCTION = 34, /* function keyword */ + TOK_EXPORT = 35, /* export keyword */ + TOK_IMPORT = 36, /* import keyword */ + TOK_IF = 37, /* if keyword */ + TOK_ELSE = 38, /* else keyword */ + TOK_SWITCH = 39, /* switch keyword */ + TOK_CASE = 40, /* case keyword */ + TOK_DEFAULT = 41, /* default keyword */ + TOK_WHILE = 42, /* while keyword */ + TOK_DO = 43, /* do keyword */ + TOK_FOR = 44, /* for keyword */ + TOK_BREAK = 45, /* break keyword */ + TOK_CONTINUE = 46, /* continue keyword */ + TOK_IN = 47, /* in keyword */ + TOK_VAR = 48, /* var keyword */ + TOK_WITH = 49, /* with keyword */ + TOK_RETURN = 50, /* return keyword */ + TOK_NEW = 51, /* new keyword */ + TOK_DELETE = 52, /* delete keyword */ + TOK_DEFSHARP = 53, /* #n= for object/array initializers */ + TOK_USESHARP = 54, /* #n# for object/array initializers */ + TOK_TRY = 55, /* try keyword */ + TOK_CATCH = 56, /* catch keyword */ + TOK_FINALLY = 57, /* finally keyword */ + TOK_THROW = 58, /* throw keyword */ + TOK_INSTANCEOF = 59, /* instanceof keyword */ TOK_RESERVED, /* reserved keywords */ TOK_LIMIT /* domain size */ } JSTokenType; -#define IS_PRIMARY_TOKEN(tt) (TOK_NAME <= (tt) && (tt) <= TOK_PRIMARY) +#define IS_PRIMARY_TOKEN(tt) \ + ((uintN)((tt) - TOK_NAME) <= (uintN)(TOK_PRIMARY - TOK_NAME)) + +struct JSTokenPtr { + uint16 index; /* index of char in physical line */ + uint16 lineno; /* physical line number */ +}; + +struct JSTokenPos { + JSTokenPtr begin; /* first character and line of token */ + JSTokenPtr end; /* index 1 past last char, last line */ +}; struct JSToken { JSTokenType type; /* char value or above enumerator */ + JSTokenPos pos; /* token position in file */ jschar *ptr; /* beginning of token in line buffer */ union { - JSAtom *atom; /* atom table entry */ + struct { + JSOp op; /* operator, for minimal parser */ + JSAtom *atom; /* atom table entry */ + } s; jsdouble dval; /* floating point number */ - JSOp op; /* operator, for minimal parser */ } u; }; +#define t_op u.s.op +#define t_atom u.s.atom +#define t_dval u.dval + typedef struct JSTokenBuf { jschar *base; /* base of line or stream buffer */ jschar *limit; /* limit for quick bounds check */ @@ -108,11 +137,12 @@ typedef struct JSTokenBuf { struct JSTokenStream { JSToken token; /* last token scanned */ JSToken pushback; /* pushed-back already-scanned token */ - uint16 newlines; /* newlines skipped during lookahead */ - uint16 flags; /* flags -- see below */ - uint16 lineno; /* current line number */ + uintN lineno; /* current line number */ uintN ungetpos; /* next free char slot in ungetbuf */ jschar ungetbuf[4]; /* at most 4, for \uXXXX lookahead */ + uintN flags; /* flags -- see below */ + ptrdiff_t linelen; /* physical linebuf segment length */ + ptrdiff_t linepos; /* linebuf offset in physical line */ JSTokenBuf linebuf; /* line buffer for diagnostics */ JSTokenBuf userbuf; /* user input buffer if !file */ JSTokenBuf tokenbuf; /* current token string buffer */ @@ -121,19 +151,20 @@ struct JSTokenStream { FILE *file; /* stdio stream if reading from file */ #endif JSPrincipals *principals; /* principals associated with given input */ +#ifdef JSD_LOWLEVEL_SOURCE + JSDContext *jsdc; /* used to cram source into debugger */ + JSDSourceText *jsdsrc; /* used to cram source into debugger */ +#endif }; /* JSTokenStream flags */ -#define TSF_ERROR 0x0001 /* fatal error while scanning */ -#define TSF_EOF 0x0002 /* hit end of file */ -#define TSF_NEWLINES 0x0004 /* tokenize newlines */ -#define TSF_FUNCTION 0x0008 /* scanning inside function body */ -#define TSF_RETURN_EXPR 0x0010 /* function has 'return expr;' */ -#define TSF_RETURN_VOID 0x0020 /* function has 'return;' */ -#define TSF_INTERACTIVE 0x0040 /* interactive parsing mode */ -#define TSF_COMMAND 0x0080 /* command parsing mode */ -#define TSF_LOOKAHEAD 0x0100 /* looking ahead for a token */ -#define TSF_REGEXP 0x0200 /* looking for a regular expression */ +#define TSF_ERROR 0x01 /* fatal error while scanning */ +#define TSF_EOF 0x02 /* hit end of file */ +#define TSF_NEWLINES 0x04 /* tokenize newlines */ +#define TSF_REGEXP 0x08 /* looking for a regular expression */ +#define TSF_INTERACTIVE 0x10 /* interactive parsing mode */ +#define TSF_NLFLAG 0x20 /* last linebuf ended with \n */ +#define TSF_CRFLAG 0x40 /* linebuf would have ended with \r */ /* * At most one non-EOF token can be pushed back onto a TokenStream between @@ -150,17 +181,14 @@ struct JSTokenStream { (ts)->pushback.type = TOK_EOF; \ PR_END_MACRO -/* - * Return the current line number by subtracted newlines skipped during - * lookahead that was pushed back and has yet to be rescanned. - */ -#define TRUE_LINENO(ts) ((ts)->lineno - (ts)->newlines) +/* Clear ts member that might point above a released cx->tempPool mark. */ +#define RESET_TOKENBUF(ts) ((ts)->tokenbuf.base = (ts)->tokenbuf.limit = NULL) /* * Create a new token stream, either from an input buffer or from a file. * Return null on file-open or memory-allocation failure. * - * NB: Both js_New{Buffer,File}TokenStream() return a pointer to transient + * NB: All of js_New{,Buffer,File}TokenStream() return a pointer to transient * memory in the current context's temp pool. This memory is deallocated via * PR_ARENA_RELEASE() after parsing is finished. */ @@ -171,8 +199,10 @@ js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, extern JS_FRIEND_API(JSTokenStream *) js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length); +#ifdef JSFILE extern JS_FRIEND_API(JSTokenStream *) -js_NewFileTokenStream(JSContext *cx, const char *filename); +js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp); +#endif extern JS_FRIEND_API(JSBool) js_CloseTokenStream(JSContext *cx, JSTokenStream *ts); @@ -202,16 +232,16 @@ js_ReportCompileError(JSContext *cx, JSTokenStream *ts, const char *format, * Look ahead one token and return its type. */ extern JSTokenType -js_PeekToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg); +js_PeekToken(JSContext *cx, JSTokenStream *ts); extern JSTokenType -js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg); +js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts); /* * Get the next token from ts. */ extern JSTokenType -js_GetToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg); +js_GetToken(JSContext *cx, JSTokenStream *ts); /* * Push back the last scanned token onto ts. @@ -223,14 +253,7 @@ js_UngetToken(JSTokenStream *ts); * Get the next token from ts if its type is tt. */ extern JSBool -js_MatchToken(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, - JSTokenType tt); - -/* - * Emit pending newline source notes counted while (mis-)matching lookahead. - */ -extern JSBool -js_FlushNewlines(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg); +js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt); PR_END_EXTERN_C diff --git a/mozilla/js/src/jsscope.c b/mozilla/js/src/jsscope.c index 32d0bfa1af9..72a6c5e7551 100644 --- a/mozilla/js/src/jsscope.c +++ b/mozilla/js/src/jsscope.c @@ -44,54 +44,58 @@ js_hash_id(const void *key) return atom->number; } +typedef struct JSScopePrivate { + JSContext *context; + JSScope *scope; +} JSScopePrivate; + PR_STATIC_CALLBACK(void *) js_alloc_scope_space(void *priv, size_t size) { - return JS_malloc(priv, size); + return JS_malloc(((JSScopePrivate *)priv)->context, size); } PR_STATIC_CALLBACK(void) js_free_scope_space(void *priv, void *item) { - JS_free(priv, item); + JS_free(((JSScopePrivate *)priv)->context, item); } PR_STATIC_CALLBACK(PRHashEntry *) js_alloc_symbol(void *priv, const void *key) { + JSScopePrivate *spriv; JSContext *cx; JSSymbol *sym; - JSAtom *atom; - cx = priv; - PR_ASSERT(JS_IS_LOCKED(cx)); + spriv = priv; + PR_ASSERT(JS_IS_SCOPE_LOCKED(spriv->scope)); + cx = spriv->context; sym = JS_malloc(cx, sizeof(JSSymbol)); if (!sym) return NULL; sym->entry.key = key; - if (!JSVAL_IS_INT((jsval)key)) { - atom = (JSAtom *)key; - js_HoldAtom(cx, atom); - } return &sym->entry; } PR_STATIC_CALLBACK(void) js_free_symbol(void *priv, PRHashEntry *he, uintN flag) { + JSScopePrivate *spriv; JSContext *cx; JSSymbol *sym, **sp; - JSProperty *prop; + JSScopeProperty *sprop; - cx = priv; - PR_ASSERT(JS_IS_LOCKED(cx)); + spriv = priv; + PR_ASSERT(JS_IS_SCOPE_LOCKED(spriv->scope)); + cx = spriv->context; sym = (JSSymbol *)he; - prop = sym->entry.value; - if (prop) { + sprop = sym->entry.value; + if (sprop) { sym->entry.value = NULL; - prop = js_DropProperty(cx, prop); - if (prop) { - for (sp = &prop->symbols; *sp; sp = &(*sp)->next) { + sprop = js_DropScopeProperty(cx, spriv->scope, sprop); + if (sprop) { + for (sp = &sprop->symbols; *sp; sp = &(*sp)->next) { if (*sp == sym) { *sp = sym->next; if (!*sp) @@ -102,11 +106,8 @@ js_free_symbol(void *priv, PRHashEntry *he, uintN flag) } } - if (flag == HT_FREE_ENTRY) { - if (!JSVAL_IS_INT(sym_id(sym))) - JS_LOCK_VOID(cx, js_DropAtom(cx, sym_atom(sym))); + if (flag == HT_FREE_ENTRY) JS_free(cx, he); - } } static PRHashAllocOps hash_scope_alloc_ops = { @@ -117,7 +118,7 @@ static PRHashAllocOps hash_scope_alloc_ops = { /************************************************************************/ PR_STATIC_CALLBACK(JSSymbol *) -js_hash_scope_lookup(JSContext *cx, JSScope *scope, jsval id, PRHashNumber hash) +js_hash_scope_lookup(JSContext *cx, JSScope *scope, jsid id, PRHashNumber hash) { PRHashTable *table = scope->data; PRHashEntry **hep; @@ -128,21 +129,21 @@ js_hash_scope_lookup(JSContext *cx, JSScope *scope, jsval id, PRHashNumber hash) return sym; } -#define SCOPE_ADD(CLASS_SPECIFIC_CODE) \ +#define SCOPE_ADD(PRIV, CLASS_SPECIFIC_CODE) \ PR_BEGIN_MACRO \ if (sym) { \ - if (sym->entry.value == prop) \ + if (sym->entry.value == sprop) \ return sym; \ if (sym->entry.value) \ - js_free_symbol(cx, &sym->entry, HT_FREE_VALUE); \ + js_free_symbol(PRIV, &sym->entry, HT_FREE_VALUE); \ } else { \ CLASS_SPECIFIC_CODE \ sym->scope = scope; \ sym->next = NULL; \ } \ - if (prop) { \ - sym->entry.value = js_HoldProperty(cx, prop); \ - for (sp = &prop->symbols; *sp; sp = &(*sp)->next) \ + if (sprop) { \ + sym->entry.value = js_HoldScopeProperty(cx, scope, sprop); \ + for (sp = &sprop->symbols; *sp; sp = &(*sp)->next) \ ; \ *sp = sym; \ } else { \ @@ -151,21 +152,23 @@ js_hash_scope_lookup(JSContext *cx, JSScope *scope, jsval id, PRHashNumber hash) PR_END_MACRO PR_STATIC_CALLBACK(JSSymbol *) -js_hash_scope_add(JSContext *cx, JSScope *scope, jsval id, JSProperty *prop) +js_hash_scope_add(JSContext *cx, JSScope *scope, jsid id, JSScopeProperty *sprop) { PRHashTable *table = scope->data; const void *key; PRHashNumber keyHash; PRHashEntry **hep; JSSymbol *sym, **sp; + JSScopePrivate *priv; - PR_ASSERT(JS_IS_LOCKED(cx)); - table->allocPriv = cx; + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); + priv = table->allocPriv; + priv->context = cx; key = (const void *)id; keyHash = js_hash_id(key); hep = PR_HashTableRawLookup(table, keyHash, key); sym = (JSSymbol *) *hep; - SCOPE_ADD( + SCOPE_ADD(priv, sym = (JSSymbol *) PR_HashTableRawAdd(table, hep, keyHash, key, NULL); if (!sym) return NULL; @@ -174,26 +177,31 @@ js_hash_scope_add(JSContext *cx, JSScope *scope, jsval id, JSProperty *prop) } PR_STATIC_CALLBACK(JSBool) -js_hash_scope_remove(JSContext *cx, JSScope *scope, jsval id) +js_hash_scope_remove(JSContext *cx, JSScope *scope, jsid id) { PRHashTable *table = scope->data; + JSScopePrivate *priv; - PR_ASSERT(JS_IS_LOCKED(cx)); - table->allocPriv = cx; + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); + priv = table->allocPriv; + priv->context = cx; return PR_HashTableRemove(table, (const void *)id); } /* Forward declaration for use by js_hash_scope_clear(). */ -extern JSScopeOps js_list_scope_ops; +extern JS_FRIEND_DATA(JSScopeOps) js_list_scope_ops; PR_STATIC_CALLBACK(void) js_hash_scope_clear(JSContext *cx, JSScope *scope) { PRHashTable *table = scope->data; + JSScopePrivate *priv; - PR_ASSERT(JS_IS_LOCKED(cx)); - table->allocPriv = cx; + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); + priv = table->allocPriv; + priv->context = cx; PR_HashTableDestroy(table); + JS_free(cx, priv); scope->ops = &js_list_scope_ops; scope->data = NULL; } @@ -208,10 +216,11 @@ JSScopeOps js_hash_scope_ops = { /************************************************************************/ PR_STATIC_CALLBACK(JSSymbol *) -js_list_scope_lookup(JSContext *cx, JSScope *scope, jsval id, PRHashNumber hash) +js_list_scope_lookup(JSContext *cx, JSScope *scope, jsid id, PRHashNumber hash) { JSSymbol *sym, **sp; + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); for (sp = (JSSymbol **)&scope->data; (sym = *sp) != 0; sp = (JSSymbol **)&sym->entry.next) { if (sym_id(sym) == id) { @@ -228,15 +237,16 @@ js_list_scope_lookup(JSContext *cx, JSScope *scope, jsval id, PRHashNumber hash) #define HASH_THRESHOLD 5 PR_STATIC_CALLBACK(JSSymbol *) -js_list_scope_add(JSContext *cx, JSScope *scope, jsval id, JSProperty *prop) +js_list_scope_add(JSContext *cx, JSScope *scope, jsid id, JSScopeProperty *sprop) { JSSymbol *list = scope->data; uint32 nsyms; JSSymbol *sym, *next, **sp; PRHashTable *table; PRHashEntry **hep; + JSScopePrivate priv; - PR_ASSERT(JS_IS_LOCKED(cx)); + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); nsyms = 0; for (sym = list; sym; sym = (JSSymbol *)sym->entry.next) { if (sym_id(sym) == id) @@ -245,9 +255,13 @@ js_list_scope_add(JSContext *cx, JSScope *scope, jsval id, JSProperty *prop) } if (nsyms >= HASH_THRESHOLD) { + JSScopePrivate *priv = JS_malloc(cx, sizeof(JSScopePrivate)); + if (!priv) return NULL; + priv->context = cx; + priv->scope = scope; table = PR_NewHashTable(nsyms, js_hash_id, PR_CompareValues, PR_CompareValues, - &hash_scope_alloc_ops, cx); + &hash_scope_alloc_ops, priv); if (table) { for (sym = list; sym; sym = next) { /* Save next for loop update, before it changes in lookup. */ @@ -264,12 +278,14 @@ js_list_scope_add(JSContext *cx, JSScope *scope, jsval id, JSProperty *prop) table->nentries = nsyms; scope->ops = &js_hash_scope_ops; scope->data = table; - return scope->ops->add(cx, scope, id, prop); + return scope->ops->add(cx, scope, id, sprop); } } - SCOPE_ADD( - sym = (JSSymbol *)js_alloc_symbol(cx, (const void *)id); + priv.context = cx; + priv.scope = scope; + SCOPE_ADD(&priv, + sym = (JSSymbol *)js_alloc_symbol(&priv, (const void *)id); if (!sym) return NULL; /* Don't set keyHash until we know we need it, above. */ @@ -280,16 +296,19 @@ js_list_scope_add(JSContext *cx, JSScope *scope, jsval id, JSProperty *prop) } PR_STATIC_CALLBACK(JSBool) -js_list_scope_remove(JSContext *cx, JSScope *scope, jsval id) +js_list_scope_remove(JSContext *cx, JSScope *scope, jsid id) { JSSymbol *sym, **sp; + JSScopePrivate priv; - PR_ASSERT(JS_IS_LOCKED(cx)); + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); for (sp = (JSSymbol **)&scope->data; (sym = *sp) != 0; sp = (JSSymbol **)&sym->entry.next) { if (sym_id(sym) == id) { *sp = (JSSymbol *)sym->entry.next; - js_free_symbol(cx, &sym->entry, HT_FREE_ENTRY); + priv.context = cx; + priv.scope = scope; + js_free_symbol(&priv, &sym->entry, HT_FREE_ENTRY); return JS_TRUE; } } @@ -300,15 +319,18 @@ PR_STATIC_CALLBACK(void) js_list_scope_clear(JSContext *cx, JSScope *scope) { JSSymbol *sym; + JSScopePrivate priv; - PR_ASSERT(JS_IS_LOCKED(cx)); + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); while ((sym = scope->data) != NULL) { scope->data = sym->entry.next; - js_free_symbol(cx, &sym->entry, HT_FREE_ENTRY); + priv.context = cx; + priv.scope = scope; + js_free_symbol(&priv, &sym->entry, HT_FREE_ENTRY); } } -JSScopeOps js_list_scope_ops = { +JSScopeOps JS_FRIEND_DATA(js_list_scope_ops) = { js_list_scope_lookup, js_list_scope_add, js_list_scope_remove, @@ -322,147 +344,72 @@ js_GetMutableScope(JSContext *cx, JSObject *obj) { JSScope *scope, *newscope; - PR_ASSERT(JS_IS_LOCKED(cx)); - scope = (JSScope *)obj->map; + scope = (JSScope *) obj->map; + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); if (scope->object == obj) return scope; - newscope = js_NewScope(cx, obj->map->clasp, obj); + newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), + obj); if (!newscope) return NULL; - obj->map = (JSObjectMap *)js_HoldScope(cx, newscope); - js_DropScope(cx, scope); + JS_LOCK_SCOPE(cx, newscope); + obj->map = js_HoldObjectMap(cx, &newscope->map); + scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); + JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); return newscope; } -#if SCOPE_TABLE -static PR_HashTable *scope_table; - -#define entry2scope(he) ((JSScope *)((char *)(he) - offsetof(JSScope, entry))) - -PR_STATIC_CALLBACK(PRHashNumber) -js_scope_hash(const void *key) -{ -} - -PR_STATIC_CALLBACK(int) -js_scope_compare(const void *v1, const void *v2) -{ -} - -PR_STATIC_CALLBACK(PRHashEntry *) -js_alloc_scope(void *priv, const void *key) -{ - JSScope *scope; - - scope = JS_malloc(priv, sizeof(JSScope)); - if (!scope) - return NULL; - return &scope->entry; -} - -PR_STATIC_CALLBACK(void) -js_free_scope(void *priv, PRHashEntry *he, uintN flag) -{ - JSScope *scope; - - scope = entry2scope(he); - JS_free(priv, scope); -} - -static PRHashAllocOps scope_table_alloc_ops = { - js_alloc_scope_space, js_free_scope_space, - js_alloc_scope, js_free_scope -}; -#endif /* SCOPE_TABLE */ - JSScope * -js_MutateScope(JSContext *cx, JSObject *obj, jsval id, - JSPropertyOp getter, JSPropertyOp setter, uintN flags, - JSProperty **propp) +js_MutateScope(JSContext *cx, JSObject *obj, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSScopeProperty **propp) { - PR_ASSERT(JS_IS_LOCKED(cx)); /* XXX pessimal */ *propp = NULL; return js_GetMutableScope(cx, obj); } JSScope * -js_NewScope(JSContext *cx, JSClass *clasp, JSObject *obj) +js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, + JSObject *obj) { JSScope *scope; -#if SCOPE_TABLE - PRHashEntry *he, **hep; - if (scope_table) { - scope_table = PR_NewHashTable(INIT_SCOPE_TABLE_SIZE, js_scope_hash, - js_scope_compare, PR_CompareValues, - &scope_table_alloc_ops, cx); - if (!scope_table) - return NULL; - } else { - scope_table->allocPriv = cx; - } - hep = PR_HashTableRawLookup(scope_table, NULL, NULL); - he = PR_HashTableRawAdd(scope_table, hep, NULL, NULL, hep); - if (!he) - return NULL; - scope = entry2scope(he); -#else scope = JS_malloc(cx, sizeof(JSScope)); if (!scope) return NULL; -#endif - scope->map.nrefs = 0; - scope->map.nslots = 0; - if (clasp->flags & JSCLASS_HAS_PRIVATE) - scope->map.freeslot = JSSLOT_PRIVATE + 1; - else - scope->map.freeslot = JSSLOT_START; - scope->map.clasp = clasp; - scope->map.props = NULL; + js_InitObjectMap(&scope->map, nrefs, ops, clasp); scope->object = obj; - scope->proptail = &scope->map.props; + scope->props = NULL; + scope->proptail = &scope->props; scope->ops = &js_list_scope_ops; scope->data = NULL; + +#ifdef JS_THREADSAFE + js_NewLock(&scope->lock); + scope->count = 0; +#ifdef DEBUG + scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL; + scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0; +#endif +#endif + return scope; } void js_DestroyScope(JSContext *cx, JSScope *scope) { - JSProperty *prop; - - PR_ASSERT(JS_IS_LOCKED(cx)); - for (prop = scope->map.props; prop; prop = prop->next) - prop->object = NULL; + JS_LOCK_SCOPE(cx, scope); scope->ops->clear(cx, scope); -#if SCOPE_TABLE - PR_HashTableRawRemove(scope_table, scope->entry.value, &scope->entry); + JS_UNLOCK_SCOPE(cx, scope); +#ifdef JS_THREADSAFE + PR_ASSERT(scope->count == 0); + js_DestroyLock(&scope->lock); #endif JS_free(cx, scope); } -JSScope * -js_HoldScope(JSContext *cx, JSScope *scope) -{ - PR_ASSERT(JS_IS_LOCKED(cx)); - PR_ASSERT(scope->map.nrefs >= 0); - scope->map.nrefs++; - return scope; -} - -JSScope * -js_DropScope(JSContext *cx, JSScope *scope) -{ - PR_ASSERT(JS_IS_LOCKED(cx)); - PR_ASSERT(scope->map.nrefs > 0); - if (--scope->map.nrefs == 0) { - js_DestroyScope(cx, scope); - return NULL; - } - return scope; -} - PRHashNumber js_HashValue(jsval v) { @@ -470,7 +417,7 @@ js_HashValue(jsval v) } jsval -js_IdToValue(jsval id) +js_IdToValue(jsid id) { JSAtom *atom; @@ -480,43 +427,39 @@ js_IdToValue(jsval id) return ATOM_KEY(atom); } -JSProperty * -js_NewProperty(JSContext *cx, JSScope *scope, jsval id, - JSPropertyOp getter, JSPropertyOp setter, uintN flags) +JSScopeProperty * +js_NewScopeProperty(JSContext *cx, JSScope *scope, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) { uint32 slot; - JSProperty *prop; + JSScopeProperty *sprop; - PR_ASSERT(JS_IS_LOCKED(cx)); + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); if (!js_AllocSlot(cx, scope->object, &slot)) return NULL; - prop = JS_malloc(cx, sizeof(JSProperty)); - if (!prop) { + sprop = JS_malloc(cx, sizeof(JSScopeProperty)); + if (!sprop) { js_FreeSlot(cx, scope->object, slot); return NULL; } - prop->nrefs = 0; - prop->id = js_IdToValue(id); - prop->getter = getter; - prop->setter = setter; - prop->slot = slot; - prop->flags = flags; - prop->spare = 0; - prop->object = scope->object; - prop->symbols = NULL; - prop->next = NULL; - prop->prevp = scope->proptail; - *scope->proptail = prop; - scope->proptail = &prop->next; - return prop; + sprop->nrefs = 0; + sprop->id = js_IdToValue(id); + sprop->getter = getter; + sprop->setter = setter; + sprop->slot = slot; + sprop->attrs = attrs; + sprop->spare = 0; + sprop->symbols = NULL; + sprop->next = NULL; + sprop->prevp = scope->proptail; + *scope->proptail = sprop; + scope->proptail = &sprop->next; + return sprop; } void -js_DestroyProperty(JSContext *cx, JSProperty *prop) +js_DestroyScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop) { - JSObject *obj; - JSScope *scope; - /* * Test whether obj was finalized before prop's last dereference. * @@ -531,45 +474,44 @@ js_DestroyProperty(JSContext *cx, JSProperty *prop) * symbol in obj's scope) goes away before obj is finalized, we must be * sure to free prop's slot and unlink it from obj's property list. */ - obj = prop->object; - if (obj) { - scope = (JSScope *)obj->map; - if (scope && scope->object) { - js_FreeSlot(cx, obj, prop->slot); - *prop->prevp = prop->next; - if (prop->next) - prop->next->prevp = prop->prevp; + if (scope) { + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); + if (scope->object) { + js_FreeSlot(cx, scope->object, sprop->slot); + *sprop->prevp = sprop->next; + if (sprop->next) + sprop->next->prevp = sprop->prevp; else - scope->proptail = prop->prevp; + scope->proptail = sprop->prevp; } } /* Purge any cached weak links to prop, then free it. */ - js_FlushPropertyCacheByProp(cx, prop); - JS_free(cx, prop); + js_FlushPropertyCacheByProp(cx, (JSProperty *)sprop); + JS_free(cx, sprop); } -JSProperty * -js_HoldProperty(JSContext *cx, JSProperty *prop) +JSScopeProperty * +js_HoldScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop) { - PR_ASSERT(JS_IS_LOCKED(cx)); - if (prop) { - PR_ASSERT(prop->nrefs >= 0); - prop->nrefs++; + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); + if (sprop) { + PR_ASSERT(sprop->nrefs >= 0); + sprop->nrefs++; } - return prop; + return sprop; } -JSProperty * -js_DropProperty(JSContext *cx, JSProperty *prop) +JSScopeProperty * +js_DropScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop) { - PR_ASSERT(JS_IS_LOCKED(cx)); - if (prop) { - PR_ASSERT(prop->nrefs > 0); - if (--prop->nrefs == 0) { - js_DestroyProperty(cx, prop); - prop = NULL; + PR_ASSERT(JS_IS_SCOPE_LOCKED(scope)); + if (sprop) { + PR_ASSERT(sprop->nrefs > 0); + if (--sprop->nrefs == 0) { + js_DestroyScopeProperty(cx, scope, sprop); + sprop = NULL; } } - return prop; + return sprop; } diff --git a/mozilla/js/src/jsscope.h b/mozilla/js/src/jsscope.h index 55e0a8430a4..a481ae93c3f 100644 --- a/mozilla/js/src/jsscope.h +++ b/mozilla/js/src/jsscope.h @@ -32,22 +32,28 @@ #include "jspubtd.h" struct JSScopeOps { - JSSymbol * (*lookup)(JSContext *cx, JSScope *scope, jsval id, + JSSymbol * (*lookup)(JSContext *cx, JSScope *scope, jsid id, PRHashNumber hash); - JSSymbol * (*add)(JSContext *cx, JSScope *scope, jsval id, - JSProperty *prop); - JSBool (*remove)(JSContext *cx, JSScope *scope, jsval id); + JSSymbol * (*add)(JSContext *cx, JSScope *scope, jsid id, + JSScopeProperty *sprop); + JSBool (*remove)(JSContext *cx, JSScope *scope, jsid id); void (*clear)(JSContext *cx, JSScope *scope); }; struct JSScope { JSObjectMap map; /* base class state */ JSObject *object; /* object that owns this scope */ - JSProperty **proptail; /* pointer to pointer to last prop */ + JSScopeProperty *props; /* property list in definition order */ + JSScopeProperty **proptail; /* pointer to pointer to last prop */ JSScopeOps *ops; /* virtual operations */ void *data; /* private data specific to ops */ -#if SCOPE_TABLE - PRHashEntry entry; +#ifdef JS_THREADSAFE + JSThinLock lock; /* binary semaphore protecting scope */ + int32 count; /* entry count for reentrancy */ +#ifdef DEBUG + const char *file[4]; /* file where lock was (re-)taken */ + unsigned int line[4]; /* line where lock was (re-)taken */ +#endif #endif }; @@ -57,61 +63,64 @@ struct JSSymbol { JSSymbol *next; /* next in type-specific list */ }; -#define sym_id(sym) ((jsval)(sym)->entry.key) +#define sym_id(sym) ((jsid)(sym)->entry.key) #define sym_atom(sym) ((JSAtom *)(sym)->entry.key) -#define sym_property(sym) ((JSProperty *)(sym)->entry.value) +#define sym_property(sym) ((JSScopeProperty *)(sym)->entry.value) -struct JSProperty { +struct JSScopeProperty { jsrefcount nrefs; /* number of referencing symbols */ jsval id; /* id passed to getter and setter */ JSPropertyOp getter; /* getter and setter methods */ JSPropertyOp setter; uint32 slot; /* index in obj->slots vector */ - uint8 flags; /* flags -- see JSPROP_* in jsapi.h */ + uint8 attrs; /* attributes, see jsapi.h JSPROP_ */ uint8 spare; /* reserved for future use */ - JSObject *object; /* object that owns this property */ JSSymbol *symbols; /* list of aliasing symbols */ - JSProperty *next; /* doubly-linked list linkage */ - JSProperty **prevp; + JSScopeProperty *next; /* doubly-linked list linkage */ + JSScopeProperty **prevp; }; +/* + * These macros are designed to decouple getter and setter from sprop, by + * passing obj2 (in whose scope sprop lives, and in whose scope getter and + * setter might be stored apart from sprop -- say in scope->opTable[i] for + * a compressed getter or setter index i that is stored in sprop). + */ +#define SPROP_GET(cx,sprop,obj,obj2,vp) ((sprop)->getter(cx,obj,sprop->id,vp)) +#define SPROP_SET(cx,sprop,obj,obj2,vp) ((sprop)->setter(cx,obj,sprop->id,vp)) + extern JSScope * js_GetMutableScope(JSContext *cx, JSObject *obj); extern JSScope * -js_MutateScope(JSContext *cx, JSObject *obj, jsval id, - JSPropertyOp getter, JSPropertyOp setter, uintN flags, - JSProperty **propp); +js_MutateScope(JSContext *cx, JSObject *obj, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSScopeProperty **propp); extern JSScope * -js_NewScope(JSContext *cx, JSClass *clasp, JSObject *obj); +js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, + JSObject *obj); extern void js_DestroyScope(JSContext *cx, JSScope *scope); -extern JSScope * -js_HoldScope(JSContext *cx, JSScope *scope); - -extern JSScope * -js_DropScope(JSContext *cx, JSScope *scope); - extern PRHashNumber js_HashValue(jsval v); extern jsval -js_IdToValue(jsval id); +js_IdToValue(jsid id); -extern JSProperty * -js_NewProperty(JSContext *cx, JSScope *scope, jsval id, - JSPropertyOp getter, JSPropertyOp setter, uintN flags); +extern JSScopeProperty * +js_NewScopeProperty(JSContext *cx, JSScope *scope, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); extern void -js_DestroyProperty(JSContext *cx, JSProperty *prop); +js_DestroyScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop); -extern JSProperty * -js_HoldProperty(JSContext *cx, JSProperty *prop); +extern JSScopeProperty * +js_HoldScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop); -extern JSProperty * -js_DropProperty(JSContext *cx, JSProperty *prop); +extern JSScopeProperty * +js_DropScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop); #endif /* jsscope_h___ */ diff --git a/mozilla/js/src/jsscript.c b/mozilla/js/src/jsscript.c index dad3fb0e734..47f0146869a 100644 --- a/mozilla/js/src/jsscript.c +++ b/mozilla/js/src/jsscript.c @@ -21,79 +21,666 @@ */ #include #include "prtypes.h" +#include "prlog.h" +#include "prprf.h" #include "jsapi.h" +#include "jsatom.h" #include "jscntxt.h" +#include "jsconfig.h" #include "jsdbgapi.h" #include "jsemit.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jsnum.h" +#include "jsopcode.h" #include "jsscript.h" +#if JS_HAS_XDR +#include "jsxdrapi.h" +#endif -JSScript * -js_NewScript(JSContext *cx, JSCodeGenerator *cg, const char *filename, - uintN lineno, JSPrincipals *principals, JSFunction *fun) +#if JS_HAS_SCRIPT_OBJECT + +#if JS_HAS_TOSOURCE +static JSBool +script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) { JSScript *script; - ptrdiff_t length; - JSNewScriptHookProc hookproc; + size_t i, j, k, n; + char buf[16]; + jschar *s, *t; + uint32 indent; + JSString *str; - length = CG_OFFSET(cg); - script = JS_malloc(cx, sizeof(JSScript) + length); + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = JS_GetPrivate(cx, obj); + + /* Let n count the source string length, j the "front porch" length. */ + j = PR_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name); + n = j + 2; + if (!script) { + /* Let k count the constructor argument string length. */ + k = 0; + } else { + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; + str = JS_DecompileScript(cx, script, "Script.prototype.toSource", + (uintN)indent); + if (!str) + return JS_FALSE; + str = js_QuoteString(cx, str, '\''); + if (!str) + return JS_FALSE; + s = str->chars; + k = str->length; + n += k; + } + + /* Allocate the source string and copy into it. */ + t = JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!t) + return JS_FALSE; + for (i = 0; i < j; i++) + t[i] = buf[i]; + for (j = 0; j < k; i++, j++) + t[i] = s[j]; + t[i++] = ')'; + t[i++] = ')'; + t[i] = 0; + + /* Create and return a JS string for t. */ + str = JS_NewUCString(cx, t, n); + if (!str) { + JS_free(cx, t); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSScript *script; + uint32 indent; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = JS_GetPrivate(cx, obj); + if (!script) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; + str = JS_DecompileScript(cx, script, "Script.prototype.toString", + (uintN)indent); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSScript *oldscript, *script; + JSString *str; + JSStackFrame *fp, *caller; + JSObject *scopeobj; + const char *file; + uintN line; + JSPrincipals *principals; + + /* If no args, leave private undefined and return early. */ + if (argc == 0) + goto out; + + /* Otherwise, the first arg is the script source to compile. */ + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + + /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ + fp = cx->fp; + caller = fp->down; + PR_ASSERT(fp->scopeChain == caller->scopeChain); + + scopeobj = NULL; + if (argc >= 2) { + if (!js_ValueToObject(cx, argv[1], &scopeobj)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(scopeobj); + } + if (!scopeobj) + scopeobj = caller->scopeChain; + + if (caller->script) { + file = caller->script->filename; + line = js_PCToLineNumber(caller->script, caller->pc); + principals = caller->script->principals; + } else { + file = NULL; + line = 0; + principals = NULL; + } + + /* Compile the new script using the caller's scope chain, a la eval(). */ + script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, + str->chars, str->length, + file, line); + if (!script) + return JS_FALSE; + + /* Swap script for obj's old script, if any. */ + oldscript = JS_GetPrivate(cx, obj); + if (!JS_SetPrivate(cx, obj, script)) { + js_DestroyScript(cx, script); + return JS_FALSE; + } + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->object = obj; +out: + /* Return the object. */ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + JSStackFrame *fp, *caller; + JSObject *scopeobj; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = JS_GetPrivate(cx, obj); + if (!script) + return JS_TRUE; + + scopeobj = NULL; + if (argc >= 2) { + if (!js_ValueToObject(cx, argv[1], &scopeobj)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(scopeobj); + } + + /* Emulate eval() by using caller's this, scope chain, and sharp array. */ + fp = cx->fp; + caller = fp->down; + if (!scopeobj) + scopeobj = caller->scopeChain; + fp->thisp = caller->thisp; + PR_ASSERT(fp->scopeChain == caller->scopeChain); + fp->sharpArray = caller->sharpArray; + return js_Execute(cx, scopeobj, script, NULL, fp, JS_FALSE, rval); +} + +#if JS_HAS_XDR +static JSBool +XDRAtom1(JSXDRState *xdr, JSAtomListElement *ale) +{ + uint32 type; + jsval value; + + if (xdr->mode == JSXDR_ENCODE) { + type = JSVAL_TAG(ATOM_KEY(ale->atom)); + value = ATOM_KEY(ale->atom); + } + + if (!JS_XDRUint32(xdr, &ale->index) || + !JS_XDRValue(xdr, &value)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + ale->atom = js_AtomizeValue(xdr->cx, value, 0); + if (!ale->atom) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +XDRAtomMap(JSXDRState *xdr, JSAtomMap *map) +{ + uint32 length; + uintN i; + + if (xdr->mode == JSXDR_ENCODE) + length = map->length; + + if (!JS_XDRUint32(xdr, &length)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + JSContext *cx; + void *mark; + JSAtomList al; + JSAtomListElement *ale; + + cx = xdr->cx; + mark = PR_ARENA_MARK(&cx->tempPool); + ATOM_LIST_INIT(&al); + for (i = 0; i < length; i++) { + PR_ARENA_ALLOCATE(ale, &cx->tempPool, sizeof(*ale)); + if (!ale || + !XDRAtom1(xdr, ale)) { + if (!ale) + JS_ReportOutOfMemory(cx); + PR_ARENA_RELEASE(&cx->tempPool, mark); + return JS_FALSE; + } + ale->next = al.list; + al.count++; + al.list = ale; + } + if (!js_InitAtomMap(cx, map, &al)) { + PR_ARENA_RELEASE(&cx->tempPool, mark); + return JS_FALSE; + } + PR_ARENA_RELEASE(&cx->tempPool, mark); + } else if (xdr->mode == JSXDR_ENCODE) { + JSAtomListElement ale; + + for (i = 0; i < map->length; i++) { + ale.atom = map->vector[i]; + ale.index = i; + if (!XDRAtom1(xdr, &ale)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic) +{ + JSScript *script; + uint32 length, lineno, depth, magicval; + + if (xdr->mode == JSXDR_ENCODE) + magicval = SCRIPT_XDRMAGIC; + if (!JS_XDRUint32(xdr, &magicval)) + return JS_FALSE; + if (magicval != SCRIPT_XDRMAGIC) { + *magic = JS_FALSE; + return JS_TRUE; + } + *magic = JS_TRUE; + if (xdr->mode == JSXDR_ENCODE) { + script = *scriptp; + length = script->length; + lineno = (uint32)script->lineno; + depth = (uint32)script->depth; + } + if (!JS_XDRUint32(xdr, &length)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) { + script = js_NewScript(xdr->cx, length); + if (!script) + return JS_FALSE; + *scriptp = script; + } + if (!JS_XDRBytes(xdr, (char **)&script->code, length) || + !XDRAtomMap(xdr, &script->atomMap) || + !JS_XDRCStringOrNull(xdr, (char **)&script->notes) || + !JS_XDRCStringOrNull(xdr, (char **)&script->filename) || + !JS_XDRUint32(xdr, &lineno) || + !JS_XDRUint32(xdr, &depth)) { + if (xdr->mode == JSXDR_DECODE) { + js_DestroyScript(xdr->cx, script); + *scriptp = NULL; + } + return JS_FALSE; + } + if (xdr->mode == JSXDR_DECODE) { + script->lineno = (uintN)lineno; + script->depth = (uintN)depth; + } + return JS_TRUE; +} + +static JSBool +script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXDRState *xdr; + JSScript *script; + JSBool ok, magic; + uint32 len; + void *buf; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = JS_GetPrivate(cx, obj); + if (!script) + return JS_TRUE; + + /* create new XDR */ + xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); + if (!xdr) + return JS_FALSE; + + /* write */ + ok = js_XDRScript(xdr, &script, &magic); + if (!ok) + goto out; + if (!magic) { + *rval = JSVAL_VOID; + goto out; + } + + buf = JS_XDRMemGetData(xdr, &len); + if (!buf) { + ok = JS_FALSE; + goto out; + } + + PR_ASSERT((prword)buf % sizeof(jschar) == 0); + len /= sizeof(jschar); + str = JS_NewUCStringCopyN(cx, (jschar *)buf, len); + if (!str) { + ok = JS_FALSE; + goto out; + } + +#if IS_BIG_ENDIAN + { + jschar *chars; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + chars = JS_GetStringChars(str); + for (i = 0; i < len; i++) + chars[i] = JSXDR_SWAB16(chars[i]); + } +#endif + *rval = STRING_TO_JSVAL(str); + +out: + JS_XDRDestroy(xdr); + return ok; +} + +static JSBool +script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXDRState *xdr; + JSString *str; + void *buf; + uint32 len; + JSScript *script, *oldscript; + JSBool ok, magic; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + if (argc == 0) + return JS_TRUE; + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + + /* create new XDR */ + xdr = JS_XDRNewMem(cx, JSXDR_DECODE); + if (!xdr) + return JS_FALSE; + + buf = JS_GetStringChars(str); + len = JS_GetStringLength(str); +#if IS_BIG_ENDIAN + { + jschar *from, *to; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + from = (jschar *)buf; + to = JS_malloc(cx, len * sizeof(jschar)); + if (!to) + return JS_FALSE; + for (i = 0; i < len; i++) + to[i] = JSXDR_SWAB16(from[i]); + buf = (char *)to; + } +#endif + len *= sizeof(jschar); + JS_XDRMemSetData(xdr, buf, len); + + /* XXXbe should magic mismatch be error, or false return value? */ + ok = js_XDRScript(xdr, &script, &magic); + if (!ok) + goto out; + if (!magic) { + *rval = JSVAL_FALSE; + goto out; + } + + /* Swap script for obj's old script, if any. */ + oldscript = JS_GetPrivate(cx, obj); + ok = JS_SetPrivate(cx, obj, script); + if (!ok) { + JS_free(cx, script); + goto out; + } + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->object = obj; + +out: + /* + * We reset the buffer to be NULL so that it doesn't free the chars + * memory owned by str (argv[0]). + */ + JS_XDRMemSetData(xdr, NULL, 0); + JS_XDRDestroy(xdr); +#if IS_BIG_ENDIAN + JS_free(cx, buf); +#endif + *rval = JSVAL_TRUE; + return ok; +} + +#endif /* JS_HAS_XDR */ + +static char js_thaw_str[] = "thaw"; + +static JSFunctionSpec script_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, script_toSource, 0}, +#endif + {js_toString_str, script_toString, 0}, + {"compile", script_compile, 2}, + {"exec", script_exec, 1}, +#if JS_HAS_XDR + {"freeze", script_freeze, 0}, + {js_thaw_str, script_thaw, 1}, +#endif /* JS_HAS_XDR */ + {0} +}; + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +static void +script_finalize(JSContext *cx, JSObject *obj) +{ + JSScript *script; + + script = JS_GetPrivate(cx, obj); + if (script) + js_DestroyScript(cx, script); +} + +static JSBool +script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); +} + +JSClass js_ScriptClass = { + "Script", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, + NULL, NULL, script_call, NULL,/*XXXbe xdr*/ +}; + +#if JS_HAS_SCRIPT_OBJECT + +static JSBool +Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* If not constructing, replace obj with a new Script object. */ + if (!cx->fp->constructing) { + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + } + return script_compile(cx, obj, argc, argv, rval); +} + +#if JS_HAS_XDR + +static JSBool +script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + if (!script_thaw(cx, obj, argc, argv, rval)) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSFunctionSpec script_static_methods[] = { + {js_thaw_str, script_static_thaw, 1}, + {0} +}; + +#else /* !JS_HAS_XDR */ + +#define script_static_methods NULL + +#endif /* !JS_HAS_XDR */ + +JSObject * +js_InitScriptClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1, + NULL, script_methods, NULL, script_static_methods); +} + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +JSScript * +js_NewScript(JSContext *cx, uint32 length) +{ + JSScript *script; + + script = JS_malloc(cx, sizeof(JSScript) + length * sizeof(jsbytecode)); + if (!script) + return NULL; + memset(script, 0, sizeof(JSScript)); + script->code = (jsbytecode *)(script + 1); + script->length = length; + return script; +} + +JSScript * +js_NewScriptFromParams(JSContext *cx, jsbytecode *code, uint32 length, + const char *filename, uintN lineno, uintN depth, + jssrcnote *notes, JSTryNote *trynotes, + JSPrincipals *principals) +{ + JSScript *script; + + script = js_NewScript(cx, length); if (!script) return NULL; - memset(script, 0, sizeof(JSScript)); - if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) { - JS_free(cx, script); - return NULL; - } + memcpy(script->code, code, length * sizeof(jsbytecode)); if (filename) { script->filename = JS_strdup(cx, filename); if (!script->filename) { - js_DestroyScript(cx, script); - return NULL; - } + js_DestroyScript(cx, script); + return NULL; + } } - script->notes = js_FinishTakingSrcNotes(cx, cg); - if (!script->notes) { - js_DestroyScript(cx, script); - return NULL; - } - script->code = (jsbytecode *)(script + 1); - memcpy(script->code, cg->base, length * sizeof(jsbytecode)); - script->length = length; - script->depth = cg->maxStackDepth; script->lineno = lineno; - script->args = cg->args; - script->vars = cg->vars; - script->javaData = 0; + script->depth = depth; + script->notes = notes; + script->trynotes = trynotes; if (principals) JSPRINCIPALS_HOLD(cx, principals); script->principals = principals; + return script; +} - hookproc = (JSNewScriptHookProc)cx->runtime->newScriptHookProc; - if (hookproc) { - (*hookproc)(cx, filename, lineno, script, fun, - cx->runtime->newScriptHookProcData); +JS_FRIEND_API(JSScript *) +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) +{ + JSTryNote *trynotes; + jssrcnote *notes; + JSScript *script; + JSRuntime *rt; + JSNewScriptHook hook; + + if (!js_FinishTakingTryNotes(cx, cg, &trynotes)) + return NULL; + notes = js_FinishTakingSrcNotes(cx, cg); + script = js_NewScriptFromParams(cx, cg->base, CG_OFFSET(cg), + cg->filename, cg->firstLine, + cg->maxStackDepth, notes, trynotes, + cg->principals); + if (!script) + return NULL; + if (!notes || !js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) { + js_DestroyScript(cx, script); + return NULL; } + /* Tell the debugger about this compiled script. */ + rt = cx->runtime; + hook = rt->newScriptHook; + if (hook) { + (*hook)(cx, cg->filename, cg->firstLine, script, fun, + rt->newScriptHookData); + } return script; } void js_DestroyScript(JSContext *cx, JSScript *script) { - JSDestroyScriptHookProc hookproc; + JSRuntime *rt; + JSDestroyScriptHook hook; - hookproc = (JSDestroyScriptHookProc)cx->runtime->destroyScriptHookProc; - if (hookproc) - (*hookproc)(cx, script, cx->runtime->destroyScriptHookProcData); + rt = cx->runtime; + hook = rt->destroyScriptHook; + if (hook) + (*hook)(cx, script, rt->destroyScriptHookData); JS_ClearScriptTraps(cx, script); js_FreeAtomMap(cx, &script->atomMap); if (js_InterpreterHooks && js_InterpreterHooks->destroyScript) js_InterpreterHooks->destroyScript(cx, script); - if (script->filename) - JS_free(cx, (void *)script->filename); - if (script->notes) - JS_free(cx, script->notes); + JS_free(cx, (void *)script->filename); + JS_free(cx, script->notes); + JS_free(cx, script->trynotes); if (script->principals) JSPRINCIPALS_DROP(cx, script->principals); JS_free(cx, script); diff --git a/mozilla/js/src/jsscript.h b/mozilla/js/src/jsscript.h index 5390d2243ae..e654813cee5 100644 --- a/mozilla/js/src/jsscript.h +++ b/mozilla/js/src/jsscript.h @@ -26,23 +26,48 @@ PR_BEGIN_EXTERN_C -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 */ - JSSymbol *args; /* formal argument list */ - JSSymbol *vars; /* local variable list */ - JSPrincipals *principals; /* principals for this script */ - void *javaData; /* extra data used by jsjava.c */ +/* + * Exception handling information. + * All fields are code offsets, relative to the beginning of the script. + */ + +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 */ }; +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 */ +}; + +extern JSClass js_ScriptClass; + +extern JSObject * +js_InitScriptClass(JSContext *cx, JSObject *obj); + extern JSScript * -js_NewScript(JSContext *cx, JSCodeGenerator *cg, const char *filename, - uintN lineno, JSPrincipals *principals, JSFunction *fun); +js_NewScript(JSContext *cx, uint32 length); + +extern JSScript * +js_NewScriptFromParams(JSContext *cx, jsbytecode *code, uint32 length, + const char *filename, uintN lineno, uintN depth, + jssrcnote *notes, JSTryNote *trynotes, + JSPrincipals *principals); + +extern JS_FRIEND_API(JSScript *) +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun); extern void js_DestroyScript(JSContext *cx, JSScript *script); @@ -59,6 +84,9 @@ js_LineNumberToPC(JSScript *script, uintN lineno); extern uintN js_GetScriptLineExtent(JSScript *script); +extern JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic); + PR_END_EXTERN_C #endif /* jsscript_h___ */ diff --git a/mozilla/js/src/jsstr.c b/mozilla/js/src/jsstr.c index 31bb4af0b50..fd43eda1f5a 100644 --- a/mozilla/js/src/jsstr.c +++ b/mozilla/js/src/jsstr.c @@ -19,11 +19,11 @@ /* * JS string type implementation. * - * In order to avoid unnecessary JS_LockGCThing/JS_UnlockGCThing calls, these + * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these * native methods store strings (possibly newborn) converted from their 'this' * parameter and arguments on the stack: 'this' conversions at argv[-1], arg * conversions at their index (argv[0], argv[1]). This is a legitimate method - * of rooting newborns that might lose their newborn root due to subsequent GC + * of rooting things that might lose their newborn root due to subsequent GC * allocations in the same native method. */ #include @@ -46,6 +46,7 @@ #include "jslock.h" #include "jsnum.h" #include "jsobj.h" +#include "jsopcode.h" #include "jsregexp.h" #include "jsstr.h" @@ -56,22 +57,6 @@ jschar js_empty_ucstr[] = {0}; JSSubString js_EmptySubString = {0, js_empty_ucstr}; -static JSBool -str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -static JSBool -str_enumerate(JSContext *cx, JSObject *obj); - -static JSBool -str_resolve(JSContext *cx, JSObject *obj, jsval id); - -static JSClass string_class = { - "String", - JSCLASS_HAS_PRIVATE, - JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, - str_enumerate, str_resolve, JS_ConvertStub, JS_FinalizeStub -}; - enum string_tinyid { STRING_LENGTH = -1 }; @@ -81,6 +66,15 @@ static JSPropertySpec string_props[] = { {0} }; +static JSBool +str_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + /* Make delete s.length fail even though length is in s.__proto__. */ + if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) + *vp = JSVAL_FALSE; + return JS_TRUE; +} + static JSBool str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { @@ -88,7 +82,7 @@ str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (!JSVAL_IS_INT(id)) return JS_TRUE; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; if (JSVAL_TO_INT(id) == STRING_LENGTH) @@ -119,17 +113,17 @@ str_enumerate(JSContext *cx, JSObject *obj) JSBool ok; jsint i; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; ok = JS_TRUE; - JS_LockGCThing(cx, str); + js_LockGCThing(cx, str); for (i = 0; i < (jsint)str->length; i++) { ok = str_resolve1(cx, obj, str, i); if (!ok) break; } - JS_UnlockGCThing(cx, str); + js_UnlockGCThing(cx, str); return ok; } @@ -141,7 +135,7 @@ str_resolve(JSContext *cx, JSObject *obj, jsval id) if (!JSVAL_IS_INT(id)) return JS_TRUE; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; slot = JSVAL_TO_INT(id); @@ -150,12 +144,96 @@ str_resolve(JSContext *cx, JSObject *obj, jsval id) return str_resolve1(cx, obj, str, slot); } +static JSClass string_class = { + "String", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, str_delProperty, str_getProperty, JS_PropertyStub, + str_enumerate, str_resolve, JS_ConvertStub, JS_FinalizeStub +}; + +#if JS_HAS_TOSOURCE + +/* + * String.prototype.quote is generic (as are most string methods), unlike + * toSource, toString, and valueOf. + */ +static JSBool +str_quote(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + str = js_QuoteString(cx, str, '"'); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSString *str; + size_t i, j, k, n; + char buf[16]; + jschar *s, *t; + + if (!JS_InstanceOf(cx, obj, &string_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_STRING(v)) + return js_obj_toSource(cx, obj, argc, argv, rval); + str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); + if (!str) + return JS_FALSE; + j = PR_snprintf(buf, sizeof buf, "(new %s(", string_class.name); + s = str->chars; + k = str->length; + n = j + k + 2; + t = JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!t) + return JS_FALSE; + for (i = 0; i < j; i++) + t[i] = buf[i]; + for (j = 0; j < k; i++, j++) + t[i] = s[j]; + t[i++] = ')'; + t[i++] = ')'; + t[i] = 0; + str = js_NewString(cx, t, n, 0); + if (!str) { + JS_free(cx, t); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + + if (!JS_InstanceOf(cx, obj, &string_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_STRING(v)) + return js_obj_toString(cx, obj, argc, argv, rval); + *rval = v; + return JS_TRUE; +} + static JSBool str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (!JS_InstanceOf(cx, obj, &string_class, argv)) return JS_FALSE; - JS_LOCK_VOID(cx, *rval = js_GetSlot(cx, obj, JSSLOT_PRIVATE)); + *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; } @@ -168,18 +246,18 @@ str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, { JSString *str; jsdouble d; - jsint length, begin, end; + jsdouble length, begin, end; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); if (argc != 0) { - if (!JS_ValueToNumber(cx, argv[0], &d)) + if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; - length = (jsint)str->length; - begin = (jsint)d; + length = str->length; + begin = js_DoubleToInteger(d); if (begin < 0) begin = 0; else if (begin > length) @@ -188,9 +266,9 @@ str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (argc == 1) { end = length; } else { - if (!JS_ValueToNumber(cx, argv[1], &d)) + if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; - end = (jsint)d; + end = js_DoubleToInteger(d); if (end < 0) end = 0; else if (end > length) @@ -198,7 +276,7 @@ str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (end < begin) { if (cx->version != JSVERSION_1_2) { /* XXX emulate old JDK1.0 java.lang.String.substring. */ - jsint tmp = begin; + jsdouble tmp = begin; begin = end; end = tmp; } else { @@ -207,8 +285,8 @@ str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } } - str = js_NewStringCopyN(cx, str->chars + begin, (size_t)(end - begin), - 0); + str = js_NewStringCopyN(cx, str->chars + (size_t)begin, + (size_t)(end - begin), 0); if (!str) return JS_FALSE; } @@ -224,7 +302,7 @@ str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, size_t i, n; jschar *s, *news; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; n = str->length; @@ -252,7 +330,7 @@ str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, size_t i, n; jschar *s, *news; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; n = str->length; @@ -280,17 +358,18 @@ str_charAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) size_t index; jschar buf[2]; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); - if (!JS_ValueToNumber(cx, argv[0], &d)) + if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; - index = (size_t)d; - if (index >= str->length) { + d = js_DoubleToInteger(d); + if (d < 0 || str->length <= d) { *rval = JS_GetEmptyStringValue(cx); } else { + index = (size_t)d; buf[0] = str->chars[index]; buf[1] = 0; str = js_NewStringCopyN(cx, buf, 1, 0); @@ -309,16 +388,20 @@ str_charCodeAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsdouble d; size_t index; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); - if (!JS_ValueToNumber(cx, argv[0], &d)) + if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; - index = (size_t)d; - *rval = (index < str->length) ? INT_TO_JSVAL((jsint)str->chars[index]) - : JS_GetNaNValue(cx); + d = js_DoubleToInteger(d); + if (d < 0 || str->length <= d) { + *rval = JS_GetNaNValue(cx); + } else { + index = (size_t)d; + *rval = INT_TO_JSVAL((jsint)str->chars[index]); + } return JS_TRUE; } @@ -330,7 +413,7 @@ js_BoyerMooreHorspool(const jschar *text, jsint textlen, jsint i, j, k, m; uint8 skip[BMH_CHARSET_SIZE]; jschar c; - + PR_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX); for (i = 0; i < BMH_CHARSET_SIZE; i++) skip[i] = (uint8)patlen; @@ -362,14 +445,14 @@ str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) const jschar *text, *pat; jsdouble d; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); text = str->chars; textlen = (jsint)str->length; - str2 = JS_ValueToString(cx, argv[0]); + str2 = js_ValueToString(cx, argv[0]); if (!str2) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str2); @@ -377,13 +460,15 @@ str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) patlen = (jsint)str2->length; if (argc > 1) { - if (!JS_ValueToNumber(cx, argv[1], &d)) + if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; - i = (jsint)d; - if (i < 0) + d = js_DoubleToInteger(d); + if (d < 0) i = 0; - else if (i > textlen) + else if (d > textlen) i = textlen; + else + i = (jsint)d; } else { i = 0; } @@ -427,14 +512,14 @@ str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsint i, j, textlen, patlen; jsdouble d; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); text = str->chars; textlen = (jsint)str->length; - str2 = JS_ValueToString(cx, argv[0]); + str2 = js_ValueToString(cx, argv[0]); if (!str2) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str2); @@ -442,15 +527,21 @@ str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, patlen = (jsint)str2->length; if (argc > 1) { - if (!JS_ValueToNumber(cx, argv[1], &d)) + if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; - i = (jsint)d; - if (i < 0) - i = 0; - else if (i > textlen - patlen) - i = textlen - patlen; + if (JSDOUBLE_IS_NaN(d)) { + i = textlen; + } else { + d = js_DoubleToInteger(d); + if (d < 0) + i = 0; + else if (d > textlen - patlen) + i = textlen - patlen; + else + i = (jsint)d; + } } else { - i = textlen; + i = textlen; } if (patlen == 0) { @@ -506,27 +597,28 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, GlobData *data, jsval *rval) { JSString *str, *src, *opt; - JSBool ok, destroyre; + JSObject *reobj; JSRegExp *re; size_t index; + JSBool ok; jsint count; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); data->str = str; - if (JSVAL_IS_REGEXP(argv[0])) { - re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); - destroyre = JS_FALSE; + if (JSVAL_IS_REGEXP(cx, argv[0])) { + reobj = JSVAL_TO_OBJECT(argv[0]); + re = JS_GetPrivate(cx, reobj); } else { - src = JS_ValueToString(cx, argv[0]); + src = js_ValueToString(cx, argv[0]); if (!src) return JS_FALSE; if (data->optarg < argc) { argv[0] = STRING_TO_JSVAL(src); - opt = JS_ValueToString(cx, argv[data->optarg]); + opt = js_ValueToString(cx, argv[data->optarg]); if (!opt) return JS_FALSE; } else { @@ -535,11 +627,13 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, re = js_NewRegExpOpt(cx, src, opt); if (!re) return JS_FALSE; - destroyre = JS_TRUE; + reobj = NULL; } data->regexp = re; - JS_LOCK(cx); + if (reobj) + JS_LOCK_OBJ(cx, reobj); + data->global = (re->flags & JSREG_GLOB) != 0; index = 0; if (data->mode == GLOB_SEARCH) { @@ -552,7 +646,7 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } else if (data->global) { ok = JS_TRUE; re->lastIndex = 0; - for (count = 0; index < str->length; count++) { + for (count = 0; index <= str->length; count++) { ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); if (!ok || *rval != JSVAL_TRUE) break; @@ -560,8 +654,8 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!ok) break; if (cx->regExpStatics.lastMatch.length == 0) { - cx->regExpStatics.rightContext.length--; - cx->regExpStatics.rightContext.chars++; + if (index == str->length) + break; index++; } } @@ -569,10 +663,12 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, ok = js_ExecuteRegExp(cx, re, str, &index, data->mode == GLOB_REPLACE, rval); } - JS_UNLOCK(cx); - if (destroyre) + if (reobj) { + JS_UNLOCK_OBJ(cx, reobj); + } else { js_DestroyRegExp(cx, re); + } return ok; } @@ -587,29 +683,23 @@ match_glob(JSContext *cx, jsint count, GlobData *data) MatchData *mdata; JSObject *arrayobj; JSSubString *matchsub; - size_t matchlen; JSString *matchstr; jsval v; mdata = (MatchData *)data; arrayobj = mdata->arrayobj; if (!arrayobj) { - arrayobj = js_NewArrayObject(cx, 0, NULL); + arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL); if (!arrayobj) return JS_FALSE; mdata->arrayobj = arrayobj; } matchsub = &cx->regExpStatics.lastMatch; - matchlen = matchsub->length; - if (matchlen == 0) - matchlen = 1; - matchstr = js_NewStringCopyN(cx, matchsub->chars, matchlen, 0); + matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length, 0); if (!matchstr) return JS_FALSE; v = STRING_TO_JSVAL(matchstr); - if (!js_SetProperty(cx, arrayobj, INT_TO_JSVAL(count), &v)) - return JS_FALSE; - return JS_TRUE; + return js_SetProperty(cx, arrayobj, INT_TO_JSVAL(count), &v); } #endif /* JS_HAS_REGEXPS */ @@ -623,12 +713,12 @@ str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) mdata.base.optarg = 1; mdata.base.mode = GLOB_MATCH; mdata.arrayobj = NULL; - if (!JS_AddRoot(cx, &mdata.arrayobj)) + if (!js_AddRoot(cx, &mdata.arrayobj, "mdata.arrayobj")) return JS_FALSE; ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval); if (ok && mdata.arrayobj) *rval = OBJECT_TO_JSVAL(mdata.arrayobj); - JS_RemoveRoot(cx, &mdata.arrayobj); + js_RemoveRoot(cx, &mdata.arrayobj); return ok; #else return str_nyi(cx, "match"); @@ -658,19 +748,20 @@ typedef struct ReplaceData { jschar *chars; /* result chars, null initially */ size_t length; /* result length, 0 initially */ jsint index; /* index in result of next replacement */ - jsint leftIndex; /* leftContext index, always 0 for JS1.2 */ + jsint leftIndex; /* left context index in base.str->chars */ } ReplaceData; static JSSubString * -interpret_dollar(JSContext *cx, jschar *dp, jschar *bp, size_t *skip) +interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip) { JSRegExpStatics *res; - jschar *cp, dc; + jschar dc, *cp; uintN num, tmp; + JSString *str; /* Allow a real backslash (literal "\\") to escape "$1" etc. */ PR_ASSERT(*dp == '$'); - if (dp > bp && dp[-1] == '\\') + if (dp > rdata->repstr->chars && dp[-1] == '\\') return NULL; /* Interpret all Perl match-induced dollar variables. */ @@ -703,6 +794,18 @@ interpret_dollar(JSContext *cx, jschar *dp, jschar *bp, size_t *skip) case '+': return &res->lastParen; case '`': + if (cx->version == JSVERSION_1_2) { + /* + * JS1.2 imitated the Perl4 bug where left context at each step + * in an iterative use of a global regexp started from last match, + * not from the start of the target string. But Perl4 does start + * $` at the beginning of the target string when it is used in a + * substitution, so we emulate that special case here. + */ + str = rdata->base.str; + res->leftContext.chars = str->chars; + res->leftContext.length = res->lastMatch.chars - str->chars; + } return &res->leftContext; case '\'': return &res->rightContext; @@ -715,39 +818,36 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) { JSString *repstr; size_t replen, skip; - jschar *bp, *dp; + jschar *dp; JSSubString *sub; #if JS_HAS_REPLACE_LAMBDA JSObject *lambda; lambda = rdata->lambda; if (lambda) { - void *mark; uintN argc, i, j, m, n, p; jsval *sp, *oldsp, rval; + void *mark; JSStackFrame *fp; JSBool ok; - /* + /* * In the lambda case, not only do we find the replacement string's * length, we compute repstr and return it via rdata for use within * do_replace. The lambda is called with arguments ($&, $1, $2, ..., * index, input), i.e., all the properties of a regexp match array. * For $&, etc., we must create string jsvals from cx->regExpStatics. - * We grab up stack space to keep the newborn strings GC-rooted. * XXXbe should use Call here and avoid re-pushing args + * We grab up stack space to keep the newborn strings GC-rooted. */ - mark = PR_ARENA_MARK(&cx->stackPool); p = rdata->base.regexp->parenCount; argc = 1 + p + 2; - PR_ARENA_ALLOCATE(sp, &cx->stackPool, (2 + argc) * sizeof(jsval)); - if (!sp) { - JS_ReportOutOfMemory(cx); + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) return JS_FALSE; - } /* Push lambda and its 'this' parameter. */ *sp++ = OBJECT_TO_JSVAL(lambda); - *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(lambda)); + *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda)); #define PUSH_REGEXP_STATIC(sub) \ PR_BEGIN_MACRO \ @@ -786,17 +886,17 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) fp = cx->fp; oldsp = fp->sp; fp->sp = sp; - ok = js_DoCall(cx, argc); + ok = js_Invoke(cx, argc, JS_FALSE); rval = fp->sp[-1]; fp->sp = oldsp; if (ok) { /* * NB: we count on the newborn string root to hold any string - * created by this JS_ValueToString that would otherwise be GC- + * created by this js_ValueToString that would otherwise be GC- * able, until we use rdata->repstr in do_replace. */ - repstr = JS_ValueToString(cx, rval); + repstr = js_ValueToString(cx, rval); if (!repstr) { ok = JS_FALSE; } else { @@ -806,16 +906,15 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) } lambda_out: - PR_ARENA_RELEASE(&cx->stackPool, mark); + js_FreeStack(cx, mark); return ok; } #endif /* JS_HAS_REPLACE_LAMBDA */ repstr = rdata->repstr; replen = repstr->length; - bp = repstr->chars; for (dp = rdata->dollar; dp; dp = js_strchr(dp + 1, '$')) { - sub = interpret_dollar(cx, dp, bp, &skip); + sub = interpret_dollar(cx, dp, rdata, &skip); if (sub) replen += sub->length - skip; } @@ -827,19 +926,19 @@ static void do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars) { JSString *repstr; - jschar *bp, *cp, *dp; + jschar *cp, *dp; size_t len, skip; JSSubString *sub; repstr = rdata->repstr; - bp = cp = repstr->chars; + cp = repstr->chars; dp = rdata->dollar; while (dp) { len = dp - cp; js_strncpy(chars, cp, len); chars += len; cp = dp; - sub = interpret_dollar(cx, dp, bp, &skip); + sub = interpret_dollar(cx, dp, rdata, &skip); if (sub) { len = sub->length; js_strncpy(chars, sub->chars, len); @@ -855,24 +954,18 @@ static JSBool replace_glob(JSContext *cx, jsint count, GlobData *data) { ReplaceData *rdata; + JSString *str; + size_t leftoff, leftlen, replen, growth; const jschar *left; - size_t leftlen, leftoff, replen, growth; jschar *chars; rdata = (ReplaceData *)data; - left = cx->regExpStatics.leftContext.chars; - leftlen = cx->regExpStatics.leftContext.length; - if (cx->version != JSVERSION_1_2) { - /* - * JS1.2 imitated the perl4.0.low bug where $` was sometimes the left - * context from the last match, not from start of string. See the big - * comment at the bottom of js_ExecuteRegExp. - */ - leftoff = rdata->leftIndex; - rdata->leftIndex = leftlen + cx->regExpStatics.lastMatch.length; - left += leftoff; - leftlen -= leftoff; - } + str = data->str; + leftoff = rdata->leftIndex; + left = str->chars + leftoff; + leftlen = cx->regExpStatics.lastMatch.chars - left; + rdata->leftIndex = cx->regExpStatics.lastMatch.chars - str->chars; + rdata->leftIndex += cx->regExpStatics.lastMatch.length; if (!find_replen(cx, rdata, &replen)) return JS_FALSE; growth = leftlen + replen; @@ -979,8 +1072,8 @@ str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * or for the next re match. In the re case, return the matched separator in * *sep, and the possibly updated offset in *ip. * - * Return -2 on error, -1 on end of string, >= 0 for a valid index if found or - * the string length if not found. + * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next + * separator occurrence if found, or str->length if no separator is found. */ static jsint find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, @@ -988,10 +1081,23 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, { jsint i, j, k; + /* + * Stop if past end of string. If at end of string, we will compare the + * null char stored there (by js_NewString*) to sep->chars[j] in the while + * loop at the end of this function, so that + * + * "ab,".split(',') => ["ab", ""] + * + * and the resulting array converts back to the string "ab," for symmetry. + * However, we ape Perl and do this only if there is a sufficiently large + * limit argument (see str_split). + */ i = *ip; + if ((size_t)i > str->length) + return -1; /* - * Perl special case for str.split(' '), only if the user has selected + * Perl4 special case for str.split(' '), only if the user has selected * JavaScript1.2 explicitly. Split on whitespace, and skip leading w/s. * Strange but true, apparently modeled after awk. * @@ -1025,74 +1131,71 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, return i; } - /* - * Stop if past end of string. If at end of string, we will compare the - * null char stored there (by js_NewString*) to sep->chars[j] in the while - * loop below, so that - * - * "ab,".split(',') => new Array("ab", "") - * - * and the resulting array converts back to the string "ab," for symmetry. - * NB: This differs from perl, which drops the trailing empty substring if - * the LIMIT argument is omitted. - */ - if ((size_t)i > str->length) - return -1; - #if JS_HAS_REGEXPS /* * Match a regular expression against the separator at or above index i. - * Return -1 at end of string instead of trying for a match, so we don't - * get stuck in a loop. + * Call js_ExecuteRegExp with true for the test argument. On successful + * match, get the separator from cx->regExpStatics.lastMatch. */ if (re) { - while ((size_t)i < str->length) { - size_t index, leftlen; - jsval rval; + size_t index; + jsval rval; - index = (size_t)i; - if (index == str->length) - return -1; - if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) - return -2; - if (rval != JSVAL_TRUE) { - sep->length = 0; - return str->length; - } - *sep = cx->regExpStatics.lastMatch; - if (sep->length == 0) - return i + 1; /* see Special case comment below */ - - leftlen = cx->regExpStatics.leftContext.length; - if (cx->version == JSVERSION_1_2) { - if (leftlen != 0) - return i + leftlen; - } else { - if (leftlen != (size_t)i) - return leftlen; - } - i += sep->length; - *ip = i; + again: + /* JS1.2 deviated from Perl by never matching at end of string. */ + if (cx->version == JSVERSION_1_2 && (size_t)i == str->length) + return -1; + index = (size_t)i; + if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) + return -2; + if (rval != JSVAL_TRUE) { + /* Mismatch: ensure our caller advances i past end of string. */ + sep->length = 1; + return str->length; } - return -1; + i = (jsint)index; + *sep = cx->regExpStatics.lastMatch; + if (sep->length == 0) { + /* + * Empty string match: never split on an empty match at the start + * of a find_split cycle. Same rule as for an empty global match + * in match_or_replace. + */ + if (i == *ip) { + /* + * "Bump-along" to avoid sticking at an empty match, but don't + * bump past end of string -- our caller must do that by adding + * sep->length to our return value. + */ + if ((size_t)i == str->length) { + sep->length = 1; + return i; + } + i++; + goto again; + } + } + PR_ASSERT((size_t)i >= sep->length); + return i - sep->length; } #endif + /* + * Deviate from ECMA by never splitting an empty string by any separator + * string into a non-empty array (an array of length 1 that contains the + * empty string). + */ + if (!JSVERSION_IS_ECMA(cx->version) && str->length == 0) + return -1; + /* * Special case: if sep is the empty string, split str into one character - * substrings. But do not split at end of string into an empty substring. - * NB: This matches perl's special case. + * substrings. Let our caller worry about whether to split once at end of + * string into an empty substring. */ if (sep->length == 0) return ((size_t)i == str->length) ? -1 : i + 1; - /* - * Imitate Perl by not splitting an empty string by a non-empty substring - * into a non-empty array. Only JS1.1 splits that way. - */ - if (cx->version != JSVERSION_1_1 && str->length == 0) - return -1; - /* * Now that we know sep is non-empty, search starting at i in str for an * occurrence of all of sep's chars. If we find them, return the index of @@ -1115,7 +1218,7 @@ static JSBool str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str, *sub; - JSObject *arrayobj; + JSObject *arrayobj, *reobj; jsval v; JSBool ok, limited; JSRegExp *re; @@ -1123,14 +1226,13 @@ str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jsdouble d; jsint len, limit, i, j, sublen; const jschar *substr; - uintN p; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); - arrayobj = JS_NewArrayObject(cx, 0, NULL); + arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL); if (!arrayobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(arrayobj); @@ -1140,34 +1242,43 @@ str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) ok = JS_SetElement(cx, arrayobj, 0, &v); } else { #if JS_HAS_REGEXPS - if (JSVAL_IS_REGEXP(argv[0])) { - re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); + if (JSVAL_IS_REGEXP(cx, argv[0])) { + reobj = JSVAL_TO_OBJECT(argv[0]); + re = JS_GetPrivate(cx, reobj); sep = &tmp; + + /* Set a magic value so we can detect a successful re match. */ + sep->chars = NULL; } else #endif { - sep = (JSSubString *)JS_ValueToString(cx, argv[0]); + sep = (JSSubString *)js_ValueToString(cx, argv[0]); if (!sep) return JS_FALSE; argv[0] = STRING_TO_JSVAL(sep); + reobj = NULL; re = NULL; } - if (argc > 1) { - if (!JS_ValueToNumber(cx, argv[1], &d)) + + /* Use the second argument as the split limit, if given. */ + /* XXX our v2 ecma spec checks against given, undefined. */ + limited = (argc > 1); + if (limited) { + if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; + + /* Clamp limit between 0 and 1 + string length. */ + d = js_DoubleToInteger(d); + if (d < 0) + d = 0; + else if (d > str->length) + d = 1 + str->length; limit = (jsint)d; - limited = JS_TRUE; - } else { - limited = JS_FALSE; } - if (re) { - /* Save and restore parenCount in case re never matches. */ - p = cx->regExpStatics.parenCount; - cx->regExpStatics.parenCount = 0; - + if (reobj) { /* Lock to protect re just in case it's shared and global. */ - JS_LOCK(cx); + JS_LOCK_OBJ(cx, reobj); } len = i = 0; @@ -1179,12 +1290,12 @@ str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) sub = js_NewStringCopyN(cx, substr, (size_t)sublen, 0); if (!sub) { ok = JS_FALSE; - goto unlock_runtime; + goto unlock_reobj; } v = STRING_TO_JSVAL(sub); ok = JS_SetElement(cx, arrayobj, len, &v); if (!ok) - goto unlock_runtime; + goto unlock_reobj; len++; #if JS_HAS_REGEXPS /* @@ -1192,7 +1303,7 @@ str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * that matched part of the delimiter in the new array, after the * split substring that was delimited. */ - if (re && cx->regExpStatics.parenCount != 0) { + if (re && sep->chars) { uintN num; JSSubString *parsub; @@ -1204,25 +1315,31 @@ str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 0); if (!sub) { ok = JS_FALSE; - goto unlock_runtime; + goto unlock_reobj; } v = STRING_TO_JSVAL(sub); ok = JS_SetElement(cx, arrayobj, len, &v); if (!ok) - goto unlock_runtime; + goto unlock_reobj; len++; } - cx->regExpStatics.parenCount = 0; + sep->chars = NULL; } #endif i = j + sep->length; + if (!JSVERSION_IS_ECMA(cx->version)) { + /* + * Deviate from ECMA to imitate Perl, which omits a final + * split unless a limit argument is given and big enough. + */ + if (!limited && (size_t)i == str->length) + break; + } } ok = (j != -2); - unlock_runtime: - if (re) { - JS_UNLOCK(cx); - cx->regExpStatics.parenCount = p; - } + unlock_reobj: + if (reobj) + JS_UNLOCK_OBJ(cx, reobj); } return ok; } @@ -1233,17 +1350,17 @@ str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #if JS_HAS_MORE_PERL_FUN JSString *str; jsdouble d; - jsint length, begin, end; + jsdouble length, begin, end; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; if (argc != 0) { - if (!JS_ValueToNumber(cx, argv[0], &d)) + if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; - length = (jsint)str->length; - begin = (jsint)d; + length = str->length; + begin = js_DoubleToInteger(d); if (begin < 0) { begin += length; if (begin < 0) @@ -1255,9 +1372,9 @@ str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (argc == 1) { end = length; } else { - if (!JS_ValueToNumber(cx, argv[1], &d)) + if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; - end = (jsint)d; + end = js_DoubleToInteger(d); if (end < 0) end = 0; end += begin; @@ -1265,8 +1382,8 @@ str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) end = length; } - str = js_NewStringCopyN(cx, str->chars + begin, (size_t)(end - begin), - 0); + str = js_NewStringCopyN(cx, str->chars + (size_t)begin, + (size_t)(end - begin), 0); if (!str) return JS_FALSE; } @@ -1319,15 +1436,6 @@ str_unpack(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return str_nyi(cx, "unpack"); #endif } - -static JSBool -str_vec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ -#if JS_HAS_MORE_PERL_FUN -#else - return str_nyi(cx, "vec"); -#endif -} #endif /* NOTYET */ #if JS_HAS_SEQUENCE_OPS @@ -1343,10 +1451,7 @@ str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jschar *chars, *newchars; uintN i; - if (argc == 0) - return JS_TRUE; - - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); @@ -1359,7 +1464,7 @@ str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) ok = JS_TRUE; for (i = 0; i < argc; i++) { - str2 = JS_ValueToString(cx, argv[i]); + str2 = js_ValueToString(cx, argv[i]); if (!str2) { ok = JS_FALSE; goto out; @@ -1393,18 +1498,18 @@ str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsdouble d; - jsint length, begin, end; + jsdouble length, begin, end; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); if (argc != 0) { - if (!JS_ValueToNumber(cx, argv[0], &d)) + if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; - length = (jsint)str->length; - begin = (jsint)d; + length = str->length; + begin = js_DoubleToInteger(d); if (begin < 0) { begin += length; if (begin < 0) @@ -1416,9 +1521,9 @@ str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (argc == 1) { end = length; } else { - if (!JS_ValueToNumber(cx, argv[1], &d)) + if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; - end = (jsint)d; + end = js_DoubleToInteger(d); if (end < 0) { end += length; if (end < 0) @@ -1426,10 +1531,12 @@ str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } else if (end > length) { end = length; } + if (end < begin) + end = begin; } - str = js_NewStringCopyN(cx, str->chars + begin, (size_t)(end - begin), - 0); + str = js_NewStringCopyN(cx, str->chars + (size_t)begin, + (size_t)(end - begin), 0); if (!str) return JS_FALSE; } @@ -1451,9 +1558,10 @@ tagify(JSContext *cx, JSObject *obj, jsval *argv, size_t beglen, endlen, parlen, taglen; size_t i, j; - JS_LOCK_VOID(cx, str = js_ObjectToString(cx, obj)); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); if (!end) end = begin; @@ -1508,7 +1616,7 @@ tagify_value(JSContext *cx, JSObject *obj, jsval *argv, { JSString *param; - param = JS_ValueToString(cx, argv[0]); + param = js_ValueToString(cx, argv[0]); if (!param) return JS_FALSE; argv[0] = STRING_TO_JSVAL(param); @@ -1595,8 +1703,13 @@ str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } static JSFunctionSpec string_methods[] = { +#if JS_HAS_TOSOURCE + {"quote", str_quote, 0}, + {js_toSource_str, str_toSource, 0}, +#endif + /* Java-like methods. */ - {js_toString_str, str_valueOf, 0}, + {js_toString_str, str_toString, 0}, {js_valueOf_str, str_valueOf, 0}, {"substring", str_substring, 2}, {"toLowerCase", str_toLowerCase, 0}, @@ -1614,7 +1727,6 @@ static JSFunctionSpec string_methods[] = { {"substr", str_substr, 2}, #ifdef NOTYET {"unpack", str_unpack, 1}, - {"vec", str_vec, 2}, #endif /* Python-esque sequence methods. */ @@ -1644,23 +1756,19 @@ static JSBool String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; - JSBool ok; if (argc > 0) { - str = JS_ValueToString(cx, argv[0]); + str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; } else { str = cx->runtime->emptyString; } - if (obj->map->clasp != &string_class) { + if (!cx->fp->constructing) { *rval = STRING_TO_JSVAL(str); return JS_TRUE; } - ok = js_SetSlot(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); - if (!ok) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); return JS_TRUE; } @@ -1677,7 +1785,7 @@ str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!chars) return JS_FALSE; for (i = 0; i < argc; i++) { - if (!JS_ValueToUint16(cx, argv[i], &code)) { + if (!js_ValueToUint16(cx, argv[i], &code)) { JS_free(cx, chars); return JS_FALSE; } @@ -1694,16 +1802,45 @@ str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } static JSFunctionSpec string_static_methods[] = { - {"fromCharCode", str_fromCharCode, 0}, + {"fromCharCode", str_fromCharCode, 1}, {0} }; static PRHashTable *deflated_string_cache; static uint32 deflated_string_cache_bytes; #ifdef JS_THREADSAFE -static PRLock *deflated_string_cache_lock; +static JSLock *deflated_string_cache_lock; #endif +JSBool +js_InitStringGlobals(void) +{ +#ifdef JS_THREADSAFE + /* Must come through here once in primordial thread to init safely! */ + if (!deflated_string_cache_lock) { + deflated_string_cache_lock = JS_NEW_LOCK(); + if (!deflated_string_cache_lock) + return JS_FALSE; + } +#endif + return JS_TRUE; +} + +void +js_FreeStringGlobals() +{ + if (deflated_string_cache) { + PR_HashTableDestroy(deflated_string_cache); + deflated_string_cache = NULL; + } +#ifdef JS_THREADSAFE + if (deflated_string_cache_lock) { + JS_DESTROY_LOCK(deflated_string_cache_lock); + deflated_string_cache_lock = NULL; + } +#endif +} + JSObject * js_InitStringClass(JSContext *cx, JSObject *obj) { @@ -1715,27 +1852,17 @@ js_InitStringClass(JSContext *cx, JSObject *obj) empty = rt->emptyString; if (!empty) { /* Make a permanently locked empty string. */ - empty = js_NewString(cx, js_empty_ucstr, 0, GCF_LOCK); + empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK); if (!empty) return NULL; rt->emptyString = empty; - -#ifdef JS_THREADSAFE - /* Must come through here once in primordial thread to init safely! */ - if (!deflated_string_cache_lock) { - deflated_string_cache_lock = PR_NewLock(); - if (!deflated_string_cache_lock) - return NULL; - } -#endif } proto = JS_InitClass(cx, obj, NULL, &string_class, String, 1, - string_props, string_methods, - NULL, string_static_methods); + string_props, string_methods, + NULL, string_static_methods); if (!proto) return NULL; - if (!js_SetSlot(cx, proto, JSSLOT_PRIVATE, STRING_TO_JSVAL(empty))) - return NULL; + OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, STRING_TO_JSVAL(empty)); return proto; } @@ -1788,16 +1915,31 @@ js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag) return str; } +PR_STATIC_CALLBACK(PRHashNumber) +js_hash_string_pointer(const void *key) +{ + return (PRHashNumber)key >> JSVAL_TAGBITS; +} + void js_FinalizeString(JSContext *cx, JSString *str) { + PRHashNumber hash; + PRHashEntry *he, **hep; + if (str->chars) { JS_free(cx, str->chars); str->chars = NULL; if (deflated_string_cache) { + hash = js_hash_string_pointer(str); JS_ACQUIRE_LOCK(deflated_string_cache_lock); - if (PR_HashTableRemove(deflated_string_cache, str)) + hep = PR_HashTableRawLookup(deflated_string_cache, hash, str); + he = *hep; + if (he) { + JS_free(cx, he->value); + PR_HashTableRawRemove(deflated_string_cache, hep, he); deflated_string_cache_bytes -= str->length; + } JS_RELEASE_LOCK(deflated_string_cache_lock); } } @@ -1812,21 +1954,24 @@ js_StringToObject(JSContext *cx, JSString *str) obj = js_NewObject(cx, &string_class, NULL, NULL); if (!obj) return NULL; - if (!js_SetSlot(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str))) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); return obj; } JSString * js_ValueToString(JSContext *cx, jsval v) { + JSObject *obj; JSString *str; if (JSVAL_IS_OBJECT(v)) { - str = js_ObjectToString(cx, JSVAL_TO_OBJECT(v)); - } else if (JSVAL_IS_STRING(v)) { + obj = JSVAL_TO_OBJECT(v); + if (!obj) + return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) + return NULL; + } + if (JSVAL_IS_STRING(v)) { str = JSVAL_TO_STRING(v); } else if (JSVAL_IS_INT(v)) { str = js_NumberToString(cx, JSVAL_TO_INT(v)); @@ -1840,6 +1985,19 @@ js_ValueToString(JSContext *cx, jsval v) return str; } +JSString * +js_ValueToSource(JSContext *cx, jsval v) +{ + if (JSVAL_IS_STRING(v)) + return js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); + if (!JSVAL_IS_PRIMITIVE(v)) { + js_TryMethod(cx, JSVAL_TO_OBJECT(v), + cx->runtime->atomState.toSourceAtom, + 0, NULL, &v); + } + return js_ValueToString(cx, v); +} + PRHashNumber js_HashString(const JSString *str) { @@ -1945,12 +2103,6 @@ js_DeflateString(JSContext *cx, const jschar *chars, size_t length) return bytes; } -PR_STATIC_CALLBACK(PRHashNumber) -js_hash_string_pointer(const void *key) -{ - return (PRHashNumber)key >> JSVAL_TAGBITS; -} - static PRHashTable * GetDeflatedStringCache(void) { @@ -3187,7 +3339,7 @@ const uint8 js_Y[] = { /* The A table has 124 entries for a total of 496 bytes. */ -const int32 js_A[] = { +const uint32 js_A[] = { 0x0001000F, /* 0 Cc, ignorable */ 0x0004000F, /* 1 Cc, whitespace */ 0x0004000C, /* 2 Zs, whitespace */ @@ -3318,14 +3470,14 @@ const int32 js_A[] = { jschar js_ToUpper(jschar c) { - int32 v = JS_CCODE(c); - return (v & 0x00100000) ? c - (v >> 22) : c; + uint32 v = JS_CCODE(c); + return (v & 0x00100000) ? c - ((int32)v >> 22) : c; } jschar js_ToLower(jschar c) { - int32 v = JS_CCODE(c); - return (v & 0x00200000) ? c + (v >> 22) : c; + uint32 v = JS_CCODE(c); + return (v & 0x00200000) ? c + ((int32)v >> 22) : c; } -#endif /* __GNUC__ */ +#endif /* !__GNUC__ */ diff --git a/mozilla/js/src/jsstr.h b/mozilla/js/src/jsstr.h index d8b8f9ea9bd..b96701c2f5d 100644 --- a/mozilla/js/src/jsstr.h +++ b/mozilla/js/src/jsstr.h @@ -52,7 +52,7 @@ extern JSSubString js_EmptySubString; /* Unicode character attribute lookup tables. */ extern const uint8 js_X[]; extern const uint8 js_Y[]; -extern const int32 js_A[]; +extern const uint32 js_A[]; /* Enumerated Unicode general category types. */ typedef enum JSCharType { @@ -87,7 +87,7 @@ typedef enum JSCharType { } JSCharType; /* Character classifying and mapping macros, based on java.lang.Character. */ -#define JS_CCODE(c) (js_A[js_Y[(js_X[(c)>>6]<<6)|((c)&0x3F)]]) +#define JS_CCODE(c) (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]]) #define JS_CTYPE(c) (JS_CCODE(c) & 0x1F) #define JS_ISALPHA(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ @@ -122,10 +122,10 @@ typedef enum JSCharType { #ifdef __GNUC__ -#define JS_TOUPPER(c) ({int32 _v = JS_CCODE(c); \ - (_v & 0x00100000) ? (c) - (_v >> 22) : (c);}) -#define JS_TOLOWER(c) ({int32 _v = JS_CCODE(c); \ - (_v & 0x00200000) ? (c) + (_v >> 22) : (c);}) +#define JS_TOUPPER(c) ({uint32 _v = JS_CCODE(c); \ + (_v & 0x00100000) ? (c) - ((int32)_v >> 22) : (c);}) +#define JS_TOLOWER(c) ({uint32 _v = JS_CCODE(c); \ + (_v & 0x00200000) ? (c) + ((int32)_v >> 22) : (c);}) #else /* !__GNUC__ */ @@ -146,6 +146,13 @@ extern jschar js_ToLower(jschar c); #define JS7_UNHEX(c) (uintN)(isdigit(c) ? (c) - '0' : 10 + tolower(c) - 'a') #define JS7_ISLET(c) ((c) < 128 && isalpha(c)) +/* Initialize truly global state associated with JS strings. */ +extern JSBool +js_InitStringGlobals(void); + +extern void +js_FreeStringGlobals(void); + /* Initialize the String class, returning its prototype object. */ extern JSObject * js_InitStringClass(JSContext *cx, JSObject *obj); @@ -177,6 +184,13 @@ js_StringToObject(JSContext *cx, JSString *str); extern JSString * js_ValueToString(JSContext *cx, jsval v); +/* + * Convert a value to its source expression, returning null after reporting + * an error, otherwise returning a new string reference. + */ +extern JSString * +js_ValueToSource(JSContext *cx, jsval v); + #ifdef HT_ENUMERATE_NEXT /* XXX don't require prhash.h */ /* * Compute a hash function from str. diff --git a/mozilla/js/src/makefile.win b/mozilla/js/src/makefile.win index 69752642443..b6472481223 100644 --- a/mozilla/js/src/makefile.win +++ b/mozilla/js/src/makefile.win @@ -50,6 +50,7 @@ DEFFILE = $(DLLNAME).def !endif LLIBS=$(LIBNSPR) +LCFLAGS= $(LCFLAGS) -DEXPORT_JS_API #//------------------------------------------------------------------------ #// @@ -69,6 +70,7 @@ OBJS = \ .\$(OBJDIR)\jsfun.obj \ .\$(OBJDIR)\jsgc.obj \ .\$(OBJDIR)\jsinterp.obj \ + .\$(OBJDIR)\jslock.obj \ .\$(OBJDIR)\jsmath.obj \ .\$(OBJDIR)\jsnum.obj \ .\$(OBJDIR)\jsobj.obj \ @@ -79,7 +81,7 @@ OBJS = \ .\$(OBJDIR)\jsscope.obj \ .\$(OBJDIR)\jsscript.obj \ .\$(OBJDIR)\jsstr.obj \ - .\$(OBJDIR)\jslock.obj \ + .\$(OBJDIR)\jsxdrapi.obj \ $(NULL) #//------------------------------------------------------------------------ diff --git a/mozilla/js/src/prconv.sed b/mozilla/js/src/prconv.sed index cc7aaf69ff2..2aaf57e4551 100644 --- a/mozilla/js/src/prconv.sed +++ b/mozilla/js/src/prconv.sed @@ -1,6 +1,7 @@ s/extern PR_PUBLIC_API/PR_EXTERN/g s/PR_PUBLIC_API/PR_IMPLEMENT/g s/prhashcode/PRHashNumber/g +s/int64/PRInt64/g s/include "prassert.h"/include "prlog.h"/g s/include "prprintf.h"/include "prprf.h"/g s/include "prtime.h"/include "prmjtime.h"/g @@ -32,3 +33,4 @@ s/^#include "prhash.h"\(.*\)/#ifndef NSPR20\ s@/\*JSSRC-SED-MAGIC-COMMENT\*/@#define NETSCAPE_INTERNAL 1\ #include "jscompat.h"@ + diff --git a/mozilla/js/src/prmjtime.c b/mozilla/js/src/prmjtime.c index e10a3847ab4..b022571bac4 100644 --- a/mozilla/js/src/prmjtime.c +++ b/mozilla/js/src/prmjtime.c @@ -1,19 +1,5 @@ -/* -*- 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 - * compliance with the NPL. You may obtain a copy of the NPL at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the NPL is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL - * for the specific language governing rights and limitations under the - * NPL. - * - * The Initial Developer of this code under the NPL is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All Rights - * Reserved. +/* -*- Mode: C; tab-width: 8 -*- + * Copyright © 1996 Netscape Communications Corporation, All Rights Reserved. */ /* @@ -390,7 +376,7 @@ PRMJ_DSTOffset(PRInt64 time) PRInt32 diff; PRInt64 maxtimet; struct tm tm; -#if defined( XP_PC ) || defined( FREEBSD ) +#if defined( XP_PC ) || defined( FREEBSD ) || defined ( HPUX9 ) struct tm *ptm; #endif PRMJTime prtm; @@ -409,7 +395,7 @@ PRMJ_DSTOffset(PRInt64 time) } LL_L2UI(local,time); PRMJ_basetime(time,&prtm); -#if defined( XP_PC ) || defined( FREEBSD ) +#if defined( XP_PC ) || defined( FREEBSD ) || defined ( HPUX9 ) ptm = localtime(&local); if(!ptm){ return LL_ZERO; @@ -532,6 +518,22 @@ PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm) { #if defined(XP_UNIX) || defined(XP_PC) || defined(XP_MAC) struct tm a; + + /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int + * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets + * confused and dumps core. NSPR20 prtime.c attempts to fill these in by + * calling mktime on the partially filled struct, but this doesn't seem to + * work as well; the result string has "can't get timezone" for ECMA-valid + * years. Might still make sense to use this, but find the range of years + * for which valid tz information exists, and map (per ECMA hint) from the + * given year into that range. + + * N.B. This hasn't been tested with anything that actually _uses_ + * tm_gmtoff; zero might be the wrong thing to set it to if you really need + * to format a time. This fix is for jsdate.c, which only uses + * PR_FormatTime to get a string representing the time zone. */ + memset(&a, 0, sizeof(struct tm)); + a.tm_sec = prtm->tm_sec; a.tm_min = prtm->tm_min; a.tm_hour = prtm->tm_hour; @@ -541,20 +543,23 @@ PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm) a.tm_year = prtm->tm_year - 1900; a.tm_yday = prtm->tm_yday; a.tm_isdst = prtm->tm_isdst; -#ifdef SUNOS4 -{ - time_t now; - struct tm *lt; - now = time((time_t *)0); - lt = localtime(&now); - if (lt == 0) { - PR_snprintf(buf, buflen, "can't get timezone"); - return 0; + /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff + * are null. This doesn't quite work, though - the timezone is off by + * tzoff + dst. (And mktime seems to return -1 for the exact dst + * changeover time.) + + * Still not sure if MKLINUX is necessary; this is borrowed from the NSPR20 + * prtime.c. I'm leaving it out - My Linux does the right thing without it + * (and the wrong thing with it) even though it has the tm_gmtoff, tm_zone + * fields. + */ + +#if defined(SUNOS4) + if (mktime(&a) == -1) { + PR_snprintf(buf, buflen, "can't get timezone"); + return 0; } - a.tm_zone = lt->tm_zone; - a.tm_gmtoff = lt->tm_gmtoff; -} #endif return strftime(buf, buflen, fmt, &a); #endif @@ -844,7 +849,7 @@ PRMJ_localtime(PRInt64 tsecs,PRMJTime *prtm) /* seconds += dstOffset;*/ #endif -#if defined(XP_PC) || defined(XP_MAC) || defined( FREEBSD ) +#if defined(XP_PC) || defined(XP_MAC) || defined( FREEBSD ) || defined( HPUX9 ) lt = *localtime(&seconds); #else localtime_r(&seconds,<); diff --git a/mozilla/js/src/prmjtime.h b/mozilla/js/src/prmjtime.h index 7d615c77324..014a5f200ed 100644 --- a/mozilla/js/src/prmjtime.h +++ b/mozilla/js/src/prmjtime.h @@ -1,19 +1,5 @@ -/* -*- 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 - * compliance with the NPL. You may obtain a copy of the NPL at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the NPL is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL - * for the specific language governing rights and limitations under the - * NPL. - * - * The Initial Developer of this code under the NPL is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All Rights - * Reserved. +/* -*- Mode: C; tab-width: 8 -*- + * Copyright © 1996 Netscape Communications Corporation, All Rights Reserved. */ #ifndef prmjtime_h___ diff --git a/mozilla/js/src/resource.h b/mozilla/js/src/resource.h index d4d84d1fe58..9301810e444 100644 --- a/mozilla/js/src/resource.h +++ b/mozilla/js/src/resource.h @@ -1,25 +1,10 @@ -// 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 -// compliance with the NPL. You may obtain a copy of the NPL at -// http://www.mozilla.org/NPL/ -// -// Software distributed under the NPL is distributed on an "AS IS" basis, -// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL -// for the specific language governing rights and limitations under the -// NPL. -// -// The Initial Developer of this code under the NPL is Netscape -// Communications Corporation. Portions created by Netscape are -// Copyright (C) 1998 Netscape Communications Corporation. All Rights -// Reserved. - //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by js3240.rc // // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101