diff --git a/js/src/imacros.c.out b/js/src/imacros.c.out index 0328a229e073..27796b97bb3d 100644 --- a/js/src/imacros.c.out +++ b/js/src/imacros.c.out @@ -537,8 +537,8 @@ static struct { }, }; static struct { - jsbytecode for_in[10]; - jsbytecode for_each[10]; + jsbytecode for_in[13]; + jsbytecode for_each[13]; jsbytecode for_in_native[10]; jsbytecode for_each_native[10]; } iter_imacros = { @@ -546,15 +546,17 @@ static struct { /* 0*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(iterator), /* 3*/ JSOP_INT8, (JSITER_ENUMERATE), /* 5*/ JSOP_CALL, 0, 1, -/* 8*/ JSOP_PUSH, -/* 9*/ JSOP_STOP, +/* 8*/ JSOP_OBJTOP, ((JSMSG_BAD_ITERATOR_RETURN) & 0xff00) >> 8, ((JSMSG_BAD_ITERATOR_RETURN) & 0xff), +/*11*/ JSOP_PUSH, +/*12*/ JSOP_STOP, }, { /* 0*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(iterator), /* 3*/ JSOP_INT8, (JSITER_ENUMERATE|JSITER_FOREACH), /* 5*/ JSOP_CALL, 0, 1, -/* 8*/ JSOP_PUSH, -/* 9*/ JSOP_STOP, +/* 8*/ JSOP_OBJTOP, ((JSMSG_BAD_ITERATOR_RETURN) & 0xff00) >> 8, ((JSMSG_BAD_ITERATOR_RETURN) & 0xff), +/*11*/ JSOP_PUSH, +/*12*/ JSOP_STOP, }, { /* 0*/ JSOP_CALLBUILTIN, ((JSBUILTIN_ObjectToIterator) & 0xff00) >> 8, ((JSBUILTIN_ObjectToIterator) & 0xff), @@ -788,7 +790,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = { 0, /* JSOP_SETCALL */ 0, /* JSOP_TRY */ 0, /* JSOP_FINALLY */ - 0, /* JSOP_UNUSED135 */ + 0, /* JSOP_OBJTOP */ 0, /* JSOP_ARGSUB */ 0, /* JSOP_ARGCNT */ 0, /* JSOP_DEFLOCALFUN */ diff --git a/js/src/imacros.jsasm b/js/src/imacros.jsasm index cdc882a64a6f..f116184e4475 100644 --- a/js/src/imacros.jsasm +++ b/js/src/imacros.jsasm @@ -583,6 +583,7 @@ callprop iterator # fun obj int8 (JSITER_ENUMERATE) # fun obj flags call 1 # iterobj + objtop (JSMSG_BAD_ITERATOR_RETURN) # iterobj push # iterobj undef stop .end @@ -591,6 +592,7 @@ callprop iterator # fun obj int8 (JSITER_ENUMERATE|JSITER_FOREACH) # fun obj flags call 1 # iterobj + objtop (JSMSG_BAD_ITERATOR_RETURN) # iterobj push # iterobj undef stop .end diff --git a/js/src/js.msg b/js/src/js.msg index 5cb98db127dc..8ff6785487ff 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -291,7 +291,7 @@ MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (") MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for") -MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 2, JSEXN_TYPEERR, "{0}.{1} returned a primitive value") +MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 1, JSEXN_TYPEERR, "{0}.__iterator__ returned a primitive value") MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace") MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}") MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 215, 1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index dd650ed92d4d..7ddcb3c8c615 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1191,24 +1191,12 @@ JS_GetOptions(JSContext *cx) return cx->options; } -#define SYNC_OPTIONS_TO_VERSION(cx) \ - JS_BEGIN_MACRO \ - if ((cx)->options & JSOPTION_XML) \ - (cx)->version |= JSVERSION_HAS_XML; \ - else \ - (cx)->version &= ~JSVERSION_HAS_XML; \ - if ((cx)->options & JSOPTION_ANONFUNFIX) \ - (cx)->version |= JSVERSION_ANONFUNFIX; \ - else \ - (cx)->version &= ~JSVERSION_ANONFUNFIX; \ - JS_END_MACRO - JS_PUBLIC_API(uint32) JS_SetOptions(JSContext *cx, uint32 options) { uint32 oldopts = cx->options; cx->options = options; - SYNC_OPTIONS_TO_VERSION(cx); + js_SyncOptionsToVersion(cx); return oldopts; } @@ -1217,7 +1205,7 @@ JS_ToggleOptions(JSContext *cx, uint32 options) { uint32 oldopts = cx->options; cx->options ^= options; - SYNC_OPTIONS_TO_VERSION(cx); + js_SyncOptionsToVersion(cx); return oldopts; } diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index f341ec4e1125..4c4790c0989c 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2164,7 +2164,7 @@ js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v) static jsval FASTCALL Array_p_push1(JSContext* cx, JSObject* obj, jsval v) { - if (OBJ_IS_DENSE_ARRAY(cx, obj) + if (OBJ_IS_DENSE_ARRAY(cx, obj) ? array_push1_dense(cx, obj, v, &v) : array_push_slowly(cx, obj, 1, &v, &v)) { return v; @@ -2229,7 +2229,6 @@ array_pop_dense(JSContext *cx, JSObject* obj, jsval *vp) return JS_FALSE; obj->fslots[JSSLOT_ARRAY_LENGTH] = index; return JS_TRUE; - } #ifdef JS_TRACER @@ -2255,7 +2254,7 @@ array_pop(JSContext *cx, uintN argc, jsval *vp) obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; - if (OBJ_IS_DENSE_ARRAY(cx, obj)) + if (OBJ_IS_DENSE_ARRAY(cx, obj)) return array_pop_dense(cx, obj, vp); return array_pop_slowly(cx, obj, vp); } diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 3866978e7ed3..bae57053e9a9 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** @@ -61,6 +61,7 @@ #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" +#include "jspubtd.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" @@ -207,6 +208,38 @@ js_InitContextThread(JSContext *cx, JSThread *thread) #endif /* JS_THREADSAFE */ +/* + * JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version + * associated with scripts, so in addition to storing them in cx->options we + * duplicate them in cx->version (script->version, etc.) and ensure each bit + * remains synchronized between the two through these two functions. + */ +void +js_SyncOptionsToVersion(JSContext* cx) +{ + if (cx->options & JSOPTION_XML) + cx->version |= JSVERSION_HAS_XML; + else + cx->version &= ~JSVERSION_HAS_XML; + if (cx->options & JSOPTION_ANONFUNFIX) + cx->version |= JSVERSION_ANONFUNFIX; + else + cx->version &= ~JSVERSION_ANONFUNFIX; +} + +inline void +js_SyncVersionToOptions(JSContext* cx) +{ + if (cx->version & JSVERSION_HAS_XML) + cx->options |= JSOPTION_XML; + else + cx->options &= ~JSOPTION_XML; + if (cx->version & JSVERSION_ANONFUNFIX) + cx->options |= JSOPTION_ANONFUNFIX; + else + cx->options &= ~JSOPTION_ANONFUNFIX; +} + void js_OnVersionChange(JSContext *cx) { @@ -221,6 +254,7 @@ void js_SetVersion(JSContext *cx, JSVersion version) { cx->version = version; + js_SyncVersionToOptions(cx); js_OnVersionChange(cx); } @@ -237,33 +271,47 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) return NULL; #endif - cx = (JSContext *) malloc(sizeof *cx); + /* + * We need to initialize the new context fully before adding it to the + * runtime list. After that it can be accessed from another thread via + * js_ContextIterator. + */ + cx = (JSContext *) calloc(1, sizeof *cx); if (!cx) return NULL; - memset(cx, 0, sizeof *cx); cx->runtime = rt; js_InitOperationLimit(cx); cx->debugHooks = &rt->globalDebugHooks; #if JS_STACK_GROWTH_DIRECTION > 0 - cx->stackLimit = (jsuword)-1; + cx->stackLimit = (jsuword) -1; #endif cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; #ifdef JS_THREADSAFE cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet; - - /* - * At this point cx is not on rt->contextList. Thus we do not need to - * prevent a race against the GC when adding cx to JSThread.contextList. - */ js_InitContextThread(cx, thread); #endif + JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0); + JS_ASSERT(cx->version == JSVERSION_DEFAULT); + VOUCH_DOES_NOT_REQUIRE_STACK(); + JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval), + &cx->scriptStackQuota); + + JS_INIT_ARENA_POOL(&cx->tempPool, "temp", + 1024, /* FIXME: bug 421435 */ + sizeof(jsdouble), &cx->scriptStackQuota); + + js_InitRegExpStatics(cx); + JS_ASSERT(cx->resolveFlags == 0); JS_LOCK_GC(rt); for (;;) { first = (rt->contextList.next == &rt->contextList); if (rt->state == JSRTS_UP) { JS_ASSERT(!first); + + /* Ensure that it is safe to update rt->contextList below. */ + js_WaitForGC(rt); break; } if (rt->state == JSRTS_DOWN) { @@ -276,26 +324,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) JS_APPEND_LINK(&cx->link, &rt->contextList); JS_UNLOCK_GC(rt); - /* - * First we do the infallible, every-time per-context initializations. - * Should a later, fallible initialization (js_InitRegExpStatics, e.g., - * or the stuff under 'if (first)' below) fail, at least the version - * and arena-pools will be valid and safe to use (say, from the last GC - * done by js_DestroyContext). - */ - cx->version = JSVERSION_DEFAULT; - VOUCH_DOES_NOT_REQUIRE_STACK(); - JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval), - &cx->scriptStackQuota); - - JS_INIT_ARENA_POOL(&cx->tempPool, "temp", - 1024, /* FIXME: bug 421435 */ - sizeof(jsdouble), &cx->scriptStackQuota); - - js_InitRegExpStatics(cx); - - cx->resolveFlags = 0; - /* * If cx is the first context on this runtime, initialize well-known atoms, * keywords, numbers, and strings. If one of these steps should fail, the @@ -437,16 +465,21 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) } } - /* Remove cx from context list first. */ JS_LOCK_GC(rt); JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); +#ifdef JS_THREADSAFE + /* + * Typically we are called outside a request, so ensure that the GC is not + * running before removing the context from rt->contextList, see bug 477021. + */ + if (cx->requestDepth == 0) + js_WaitForGC(rt); + js_RevokeGCLocalFreeLists(cx); +#endif JS_REMOVE_LINK(&cx->link); last = (rt->contextList.next == &rt->contextList); if (last) rt->state = JSRTS_LANDING; -#ifdef JS_THREADSAFE - js_RevokeGCLocalFreeLists(cx); -#endif JS_UNLOCK_GC(rt); if (last) { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 471f4ce5ada4..2d1980758598 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1157,6 +1157,14 @@ js_InitThreadPrivateIndex(void (*ptr)(void *)); extern JSBool js_CleanupThreadPrivateData(); +/* + * Ensures the JSOPTION_XML and JSOPTION_ANONFUNFIX bits of cx->options are + * reflected in cx->version, since each bit must travel with a script that has + * it set. + */ +extern void +js_SyncOptionsToVersion(JSContext *cx); + /* * Common subroutine of JS_SetVersion and js_SetVersion, to update per-context * data that depends on version. diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index cc8fc4bd0bc3..2e05db99c71e 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -6435,6 +6435,14 @@ js_Interpret(JSContext *cx) } END_CASE(JSOP_PRIMTOP) + BEGIN_CASE(JSOP_OBJTOP) + lval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(lval)) { + js_ReportValueError(cx, GET_UINT16(regs.pc), -1, lval, NULL); + goto error; + } + END_CASE(JSOP_OBJTOP) + BEGIN_CASE(JSOP_INSTANCEOF) rval = FETCH_OPND(-1); if (JSVAL_IS_PRIMITIVE(rval) || @@ -6884,7 +6892,6 @@ js_Interpret(JSContext *cx) L_JSOP_DEFXMLNS: # endif - L_JSOP_UNUSED135: L_JSOP_UNUSED203: L_JSOP_UNUSED204: L_JSOP_UNUSED205: diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 21bbc887c460..e2fdb7fa4ee9 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -416,12 +416,8 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) goto bad; } if (JSVAL_IS_PRIMITIVE(*vp)) { - const char *printable = js_AtomToPrintableString(cx, atom); - if (printable) { - js_ReportValueError2(cx, JSMSG_BAD_ITERATOR_RETURN, - JSDVG_SEARCH_STACK, *vp, NULL, - printable); - } + js_ReportValueError(cx, JSMSG_BAD_ITERATOR_RETURN, + JSDVG_SEARCH_STACK, *vp, NULL); goto bad; } } @@ -982,8 +978,6 @@ generator_op(JSContext *cx, JSGeneratorOp op, jsval *vp, uintN argc) } } - js_LeaveTrace(cx); - arg = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0) ? vp[2] : JSVAL_VOID; diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp index 867d0bdecd07..0297a626581e 100644 --- a/js/src/jslock.cpp +++ b/js/src/jslock.cpp @@ -303,7 +303,7 @@ js_FinishLock(JSThinLock *tl) #include #include "jsdhash.h" -static FILE *logfp; +static FILE *logfp = NULL; static JSDHashTable logtbl; typedef struct logentry { @@ -314,7 +314,7 @@ typedef struct logentry { } logentry; static void -logit(JSScope *scope, char op, const char *file, int line) +logit(JSTitle *title, char op, const char *file, int line) { logentry *entry; @@ -324,35 +324,35 @@ logit(JSScope *scope, char op, const char *file, int line) return; setvbuf(logfp, NULL, _IONBF, 0); } - fprintf(logfp, "%p %c %s %d\n", scope, op, file, line); + fprintf(logfp, "%p %d %c %s %d\n", title, title->u.count, op, file, line); if (!logtbl.entryStore && !JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL, sizeof(logentry), 100)) { return; } - entry = (logentry *) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_ADD); + entry = (logentry *) JS_DHashTableOperate(&logtbl, title, JS_DHASH_ADD); if (!entry) return; - entry->stub.key = scope; + entry->stub.key = title; entry->op = op; entry->file = file; entry->line = line; } void -js_unlog_scope(JSScope *scope) +js_unlog_title(JSTitle *title) { if (!logtbl.entryStore) return; - (void) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_REMOVE); + (void) JS_DHashTableOperate(&logtbl, title, JS_DHASH_REMOVE); } -# define LOGIT(scope,op) logit(scope, op, __FILE__, __LINE__) +# define LOGIT(title,op) logit(title, op, __FILE__, __LINE__) #else -# define LOGIT(scope,op) /* nothing */ +# define LOGIT(title, op) /* nothing */ #endif /* DEBUG_SCOPE_COUNT */ @@ -665,7 +665,7 @@ js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) if (!NativeCompareAndSwap(&tl->owner, me, 0)) { /* Assert that scope locks never revert to flyweight. */ JS_ASSERT(title->ownercx != cx); - LOGIT(scope, '1'); + LOGIT(title, '1'); title->u.count = 1; js_UnlockObj(cx, obj); } @@ -755,7 +755,7 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v) if (!NativeCompareAndSwap(&tl->owner, me, 0)) { /* Assert that scope locks never revert to flyweight. */ JS_ASSERT(title->ownercx != cx); - LOGIT(scope, '1'); + LOGIT(title, '1'); title->u.count = 1; js_UnlockObj(cx, obj); } @@ -1211,7 +1211,7 @@ js_UnlockTitle(JSContext *cx, JSTitle *title) JS_ASSERT(0); /* unbalanced unlock */ return; } - LOGIT(scope, '-'); + LOGIT(title, '-'); if (--title->u.count == 0) ThinUnlock(&title->lock, me); } @@ -1279,7 +1279,7 @@ js_TransferTitle(JSContext *cx, JSTitle *oldtitle, JSTitle *newtitle) /* * Reset oldtitle's lock state so that it is completely unlocked. */ - LOGIT(oldscope, '0'); + LOGIT(oldtitle, '0'); oldtitle->u.count = 0; ThinUnlock(&oldtitle->lock, CX_THINLOCK_ID(cx)); } @@ -1350,6 +1350,10 @@ js_InitTitle(JSContext *cx, JSTitle *title) void js_FinishTitle(JSContext *cx, JSTitle *title) { +#ifdef DEBUG_SCOPE_COUNT + js_unlog_title(title); +#endif + #ifdef JS_THREADSAFE /* Title must be single-threaded at this point, so set ownercx. */ JS_ASSERT(title->u.count == 0); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 25455f628c2a..30a4cff4ca94 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -353,7 +353,12 @@ OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16 OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 2, 0, JOF_BYTE) -OPDEF(JSOP_UNUSED135, 135,"unused135", NULL, 1, 0, 0, 0, JOF_BYTE) +/* + * Ensure that the value on the top of the stack is an object. The one + * argument is an error message, defined in js.msg, that takes one parameter + * (the decompilation of the primitive value). + */ +OPDEF(JSOP_OBJTOP, 135,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16) /* * Bytecodes that avoid making an arguments object in most cases: diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index c6a13450f74a..941733062f39 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -174,11 +174,6 @@ js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, return scope; } -#ifdef DEBUG_SCOPE_COUNT -extern void -js_unlog_scope(JSScope *scope); -#endif - #if defined DEBUG || defined JS_DUMP_PROPTREE_STATS # include "jsprf.h" # define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr) @@ -189,10 +184,6 @@ js_unlog_scope(JSScope *scope); void js_DestroyScope(JSContext *cx, JSScope *scope) { -#ifdef DEBUG_SCOPE_COUNT - js_unlog_scope(scope); -#endif - #ifdef JS_THREADSAFE js_FinishTitle(cx, &scope->title); #endif diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 348e07c1884b..557f937ac7c3 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -60,6 +60,7 @@ #include "jsparse.h" #include "jsscope.h" #include "jsscript.h" +#include "jstracer.h" #if JS_HAS_XDR #include "jsxdrapi.h" #endif @@ -1607,6 +1608,9 @@ js_DestroyScript(JSContext *cx, JSScript *script) JS_ASSERT(script->owner == cx->thread); #endif js_FlushPropertyCacheForScript(cx, script); +#ifdef JS_TRACER + js_FlushScriptFragments(cx, script); +#endif } } diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 71c11f0f6951..4131d2697812 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -546,14 +546,33 @@ getLoop(JSTraceMonitor* tm, const void *ip, uint32 globalShape) return getVMFragment(tm, ip, globalShape); } +static inline void* +placementNewInLirBuffer(size_t size, nanojit::LirBuffer *buf) +{ + LirBufWriter writer(buf); + char *mem = (char*) writer.skip(size)->payload(); + if (!mem) + return NULL; + memset (mem, 0, size); + return mem; +} + +void* +avmplus::GCObject::operator new(size_t size, nanojit::LirBuffer *buf) +{ + return placementNewInLirBuffer(size, buf); +} + +void* +operator new(size_t size, nanojit::LirBuffer *buf) +{ + return placementNewInLirBuffer(size, buf); +} + static Fragment* getAnchor(JSTraceMonitor* tm, const void *ip, uint32 globalShape) { - LirBufWriter writer(tm->lirbuf); - char *fragmem = (char*) writer.skip(sizeof(VMFragment))->payload(); - if (!fragmem) - return NULL; - VMFragment *f = new (fragmem) VMFragment(ip, globalShape); + VMFragment *f = new (tm->lirbuf) VMFragment(ip, globalShape); JS_ASSERT(f); Fragment *p = getVMFragment(tm, ip, globalShape); @@ -1256,16 +1275,6 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag } } -TreeInfo::~TreeInfo() -{ - UnstableExit* temp; - - while (unstableExits) { - temp = unstableExits->next; - delete unstableExits; - unstableExits = temp; - } -} TraceRecorder::~TraceRecorder() { @@ -1282,7 +1291,6 @@ TraceRecorder::~TraceRecorder() if (fragment) { if (wasRootFragment && !fragment->root->code()) { JS_ASSERT(!fragment->root->vmprivate); - delete treeInfo; } if (trashSelf) @@ -1290,8 +1298,6 @@ TraceRecorder::~TraceRecorder() for (unsigned int i = 0; i < whichTreesToTrash.length(); i++) js_TrashTree(cx, whichTreesToTrash.get(i)); - } else if (wasRootFragment) { - delete treeInfo; } #ifdef DEBUG delete verbose_filter; @@ -2603,7 +2609,7 @@ TraceRecorder::closeLoop(JSTraceMonitor* tm, bool& demote) */ debug_only_v(printf("Trace has unstable loop variable with no stable peer, " "compiling anyway.\n");) - UnstableExit* uexit = new UnstableExit; + UnstableExit* uexit = new (tm->lirbuf) UnstableExit; uexit->fragment = fragment; uexit->exit = exit; uexit->next = treeInfo->unstableExits; @@ -2708,7 +2714,6 @@ TraceRecorder::joinEdgesToEntry(Fragmento* fragmento, Fragment* peer_root) } if (remove) { *unext = uexit->next; - delete uexit; uexit = *unext; } else { unext = &uexit->next; @@ -2952,12 +2957,6 @@ nanojit::LirNameMap::formatGuard(LIns *i, char *out) } #endif -void -nanojit::Fragment::onDestroy() -{ - delete (TreeInfo *)vmprivate; -} - static JS_REQUIRES_STACK bool js_DeleteRecorder(JSContext* cx) { @@ -3076,7 +3075,6 @@ js_TrashTree(JSContext* cx, Fragment* f) unsigned length = ti->dependentTrees.length(); for (unsigned n = 0; n < length; ++n) js_TrashTree(cx, data[n]); - delete ti; JS_ASSERT(!f->code() && !f->vmprivate); } @@ -3320,7 +3318,7 @@ js_RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f, Fragment* outer, JS_ASSERT(!f->code() && !f->vmprivate); /* setup the VM-private treeInfo structure for this fragment */ - TreeInfo* ti = new (&gc) TreeInfo(f, globalShape, globalSlots); + TreeInfo* ti = new (tm->lirbuf) TreeInfo(f, globalShape, globalSlots); /* capture the coerced type of each active slot in the type map */ ti->typeMap.captureTypes(cx, *globalSlots, 0/*callDepth*/); @@ -3443,7 +3441,6 @@ js_AttemptToStabilizeTree(JSContext* cx, VMSideExit* exit, Fragment* outer) for (UnstableExit* uexit = from_ti->unstableExits; uexit != NULL; uexit = uexit->next) { if (uexit->exit == exit) { *tail = uexit->next; - delete uexit; bound = true; break; } @@ -4591,6 +4588,20 @@ js_FlushJITOracle(JSContext* cx) oracle.clear(); } +extern JS_REQUIRES_STACK void +js_FlushScriptFragments(JSContext* cx, JSScript* script) +{ + if (!TRACING_ENABLED(cx)) + return; + debug_only_v(printf("Flushing fragments for script %p.\n", script);) + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { + VMFragment *f = tm->vmfragments[i]; + if (f && JS_UPTRDIFF(f->ip, script->code) < script->length) + tm->vmfragments[i] = NULL; + } +} + extern JS_REQUIRES_STACK void js_FlushJITCache(JSContext* cx) { @@ -5530,7 +5541,10 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2 JSAtom* atom; JSPropCacheEntry* entry; PROPERTY_CACHE_TEST(cx, pc, aobj, obj2, entry, atom); - if (atom) { + if (!atom) { + // Null atom means that obj2 is locked and must now be unlocked. + JS_UNLOCK_OBJ(cx, obj2); + } else { // Miss: pre-fill the cache for the interpreter, as well as for our needs. // FIXME: 452357 - correctly propagate exceptions into the interpreter from // js_FindPropertyHelper, js_LookupPropertyWithFlags, and elsewhere. @@ -6432,6 +6446,13 @@ TraceRecorder::record_JSOP_PRIMTOP() return true; } +JS_REQUIRES_STACK bool +TraceRecorder::record_JSOP_OBJTOP() +{ + // See the comment in record_JSOP_PRIMTOP. + return true; +} + JSBool js_Array(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval); @@ -9098,10 +9119,6 @@ ObjectToIterator_tn(JSContext* cx, jsbytecode* pc, JSObject *obj, int32 flags) cx->builtinStatus |= JSBUILTIN_ERROR; return NULL; } - if (OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_GeneratorClass) { - js_LeaveTrace(cx); - return NULL; - } return JSVAL_TO_OBJECT(v); } @@ -9376,7 +9393,6 @@ InitIMacroCode() return false; \ } -UNUSED(135) UNUSED(203) UNUSED(204) UNUSED(205) diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 1cc1f3c87559..b7add3424a51 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -296,7 +296,6 @@ public: branchCount(0), unstableExits(NULL) {} - ~TreeInfo(); inline unsigned nGlobalTypes() { return typeMap.length() - nStackTypes; @@ -602,6 +601,9 @@ js_InitJIT(JSTraceMonitor *tm); extern void js_FinishJIT(JSTraceMonitor *tm); +extern void +js_FlushScriptFragments(JSContext* cx, JSScript* script); + extern void js_FlushJITCache(JSContext* cx); diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp index 5d7eb88d6ff7..4b0a0aa68617 100644 --- a/js/src/jsutil.cpp +++ b/js/src/jsutil.cpp @@ -210,7 +210,7 @@ JS_DumpHistogram(JSBasicStats *bs, FILE *fp) #endif /* JS_BASIC_STATS */ -#if defined DEBUG_notme && defined XP_UNIX +#if defined(DEBUG_notme) && defined(XP_UNIX) #define __USE_GNU 1 #include @@ -314,7 +314,7 @@ CallTree(void **bp) return site; } -JSCallsite * +JS_FRIEND_API(JSCallsite *) JS_Backtrace(int skip) { void **bp, **bpdown; @@ -342,4 +342,14 @@ JS_Backtrace(int skip) return CallTree(bp); } -#endif /* DEBUG_notme && XP_UNIX */ +JS_FRIEND_API(void) +JS_DumpBacktrace(JSCallsite *trace) +{ + while (trace) { + fprintf(stdout, "%s [%s +0x%X]\n", trace->name, trace->library, + trace->offset); + trace = trace->parent; + } +} + +#endif /* defined(DEBUG_notme) && defined(XP_UNIX) */ diff --git a/js/src/jsutil.h b/js/src/jsutil.h index a6c329c8a045..7d072bf125cf 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -144,7 +144,7 @@ JS_DumpHistogram(JSBasicStats *bs, FILE *fp); #endif /* JS_BASIC_STATS */ -#ifdef XP_UNIX +#if defined(DEBUG_notme) && defined(XP_UNIX) typedef struct JSCallsite JSCallsite; @@ -159,7 +159,11 @@ struct JSCallsite { void *handy; }; -extern JSCallsite *JS_Backtrace(int skip); +extern JS_FRIEND_API(JSCallsite *) +JS_Backtrace(int skip); + +extern JS_FRIEND_API(void) +JS_DumpBacktrace(JSCallsite *trace); #endif diff --git a/js/src/nanojit/Fragmento.cpp b/js/src/nanojit/Fragmento.cpp index 2e24234a9139..7c4ccfb31d5f 100644 --- a/js/src/nanojit/Fragmento.cpp +++ b/js/src/nanojit/Fragmento.cpp @@ -522,7 +522,6 @@ namespace nanojit Fragment::~Fragment() { - onDestroy(); NanoAssert(_pages == 0); } diff --git a/js/src/nanojit/Fragmento.h b/js/src/nanojit/Fragmento.h index a01f6ad591d2..f16ad441c19c 100644 --- a/js/src/nanojit/Fragmento.h +++ b/js/src/nanojit/Fragmento.h @@ -191,7 +191,6 @@ namespace nanojit void releaseTreeMem(Fragmento* frago); bool isAnchor() { return anchor == this; } bool isRoot() { return root == this; } - void onDestroy(); verbose_only( uint32_t _called; ) verbose_only( uint32_t _native; ) diff --git a/js/src/nanojit/avmplus.h b/js/src/nanojit/avmplus.h index d3736b28776c..16330f6dad9f 100644 --- a/js/src/nanojit/avmplus.h +++ b/js/src/nanojit/avmplus.h @@ -145,6 +145,10 @@ static __inline__ unsigned long long rdtsc(void) struct JSContext; +namespace nanojit { + class LirBuffer; +} + namespace avmplus { class GC; @@ -158,13 +162,8 @@ namespace avmplus { return calloc(1, size); } - inline void* - operator new(size_t size, char* c) - { - // We use placement-new in LIR buffers sometimes. - memset(c, 0, size); - return c; - } + // We use placement-new in LIR buffers sometimes. + void* operator new(size_t size, nanojit::LirBuffer *buf); static void operator delete (void *gcObject) { diff --git a/js/src/trace-test.js b/js/src/trace-test.js index 0f7c08d91887..5d2f4bd5aa32 100644 --- a/js/src/trace-test.js +++ b/js/src/trace-test.js @@ -4327,6 +4327,19 @@ function testSwitchUndefined() testSwitchUndefined.expected = 5; test(testSwitchUndefined); +function testGeneratorDeepBail() { + function g() { yield 2; } + var iterables = [[1], [], [], [], g()]; + + var total = 0; + for (let i = 0; i < iterables.length; i++) + for each (let j in iterables[i]) + total += j; + return total; +} +testGeneratorDeepBail.expected = 3; +test(testGeneratorDeepBail); + /***************************************************************************** * * * _____ _ _ _____ ______ _____ _______ *